changeset 20587:f502455965c0 v8.2.0847

patch 8.2.0847: typval related code is spread out Commit: https://github.com/vim/vim/commit/367d59e6ba65cf554d167933775fa17e40dcc6a7 Author: Bram Moolenaar <Bram@vim.org> Date: Sat May 30 17:06:14 2020 +0200 patch 8.2.0847: typval related code is spread out Problem: Typval related code is spread out. Solution: Move code to new typval.c file. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/6093)
author Bram Moolenaar <Bram@vim.org>
date Sat, 30 May 2020 17:15:03 +0200
parents fed0050be1a1
children 7d6b68ad708c
files Filelist src/Make_cyg_ming.mak src/Make_morph.mak src/Make_mvc.mak src/Make_vms.mms src/Makefile src/README.md src/eval.c src/evalfunc.c src/globals.h src/proto.h src/proto/eval.pro src/proto/evalfunc.pro src/proto/typval.pro src/typval.c src/version.c
diffstat 16 files changed, 1569 insertions(+), 1534 deletions(-) [+]
line wrap: on
line diff
--- 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 \
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -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 \
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -107,6 +107,7 @@ SRC =	arabic.c						\
 	textobject.c						\
 	textprop.c						\
 	time.c							\
+	typval.c						\
 	ui.c							\
 	undo.c							\
 	usercmd.c						\
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -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 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l
 
 $(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 \
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -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]c
 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
--- a/src/Makefile
+++ b/src/Makefile
@@ -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/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 \
--- a/src/README.md
+++ b/src/README.md
@@ -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
--- a/src/eval.c
+++ b/src/eval.c
@@ -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,
 
 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.
@@ -3304,403 +3300,6 @@ eval_index(
 }
 
 /*
- * Get an option value.
- * "arg" points to the '&' or '+' before the option name.
- * "arg" is advanced to character after the option name.
- * Return OK or FAIL.
- */
-    int
-get_option_tv(
-    char_u	**arg,
-    typval_T	*rettv,	// when NULL, only check if option exists
-    int		evaluate)
-{
-    char_u	*option_end;
-    long	numval;
-    char_u	*stringval;
-    int		opt_type;
-    int		c;
-    int		working = (**arg == '+');    // has("+option")
-    int		ret = OK;
-    int		opt_flags;
-
-    /*
-     * Isolate the option name and find its value.
-     */
-    option_end = find_option_end(arg, &opt_flags);
-    if (option_end == NULL)
-    {
-	if (rettv != NULL)
-	    semsg(_("E112: Option name missing: %s"), *arg);
-	return FAIL;
-    }
-
-    if (!evaluate)
-    {
-	*arg = option_end;
-	return OK;
-    }
-
-    c = *option_end;
-    *option_end = NUL;
-    opt_type = get_option_value(*arg, &numval,
-			       rettv == NULL ? NULL : &stringval, opt_flags);
-
-    if (opt_type == -3)			// invalid name
-    {
-	if (rettv != NULL)
-	    semsg(_(e_unknown_option), *arg);
-	ret = FAIL;
-    }
-    else if (rettv != NULL)
-    {
-	if (opt_type == -2)		// hidden string option
-	{
-	    rettv->v_type = VAR_STRING;
-	    rettv->vval.v_string = NULL;
-	}
-	else if (opt_type == -1)	// hidden number option
-	{
-	    rettv->v_type = VAR_NUMBER;
-	    rettv->vval.v_number = 0;
-	}
-	else if (opt_type == 1)		// number option
-	{
-	    rettv->v_type = VAR_NUMBER;
-	    rettv->vval.v_number = numval;
-	}
-	else				// string option
-	{
-	    rettv->v_type = VAR_STRING;
-	    rettv->vval.v_string = stringval;
-	}
-    }
-    else if (working && (opt_type == -2 || opt_type == -1))
-	ret = FAIL;
-
-    *option_end = c;		    // put back for error messages
-    *arg = option_end;
-
-    return ret;
-}
-
-/*
- * Allocate a variable for a number constant.  Also deals with "0z" for blob.
- * Return OK or FAIL.
- */
-    int
-get_number_tv(
-	char_u	    **arg,
-	typval_T    *rettv,
-	int	    evaluate,
-	int	    want_string UNUSED)
-{
-    int		len;
-#ifdef FEAT_FLOAT
-    char_u	*p;
-    int		get_float = FALSE;
-
-    // We accept a float when the format matches
-    // "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?".  This is very
-    // strict to avoid backwards compatibility problems.
-    // With script version 2 and later the leading digit can be
-    // omitted.
-    // Don't look for a float after the "." operator, so that
-    // ":let vers = 1.2.3" doesn't fail.
-    if (**arg == '.')
-	p = *arg;
-    else
-	p = skipdigits(*arg + 1);
-    if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
-    {
-	get_float = TRUE;
-	p = skipdigits(p + 2);
-	if (*p == 'e' || *p == 'E')
-	{
-	    ++p;
-	    if (*p == '-' || *p == '+')
-		++p;
-	    if (!vim_isdigit(*p))
-		get_float = FALSE;
-	    else
-		p = skipdigits(p + 1);
-	}
-	if (ASCII_ISALPHA(*p) || *p == '.')
-	    get_float = FALSE;
-    }
-    if (get_float)
-    {
-	float_T	f;
-
-	*arg += string2float(*arg, &f);
-	if (evaluate)
-	{
-	    rettv->v_type = VAR_FLOAT;
-	    rettv->vval.v_float = f;
-	}
-    }
-    else
-#endif
-    if (**arg == '0' && ((*arg)[1] == 'z' || (*arg)[1] == 'Z'))
-    {
-	char_u  *bp;
-	blob_T  *blob = NULL;  // init for gcc
-
-	// Blob constant: 0z0123456789abcdef
-	if (evaluate)
-	    blob = blob_alloc();
-	for (bp = *arg + 2; vim_isxdigit(bp[0]); bp += 2)
-	{
-	    if (!vim_isxdigit(bp[1]))
-	    {
-		if (blob != NULL)
-		{
-		    emsg(_("E973: Blob literal should have an even number of hex characters"));
-		    ga_clear(&blob->bv_ga);
-		    VIM_CLEAR(blob);
-		}
-		return FAIL;
-	    }
-	    if (blob != NULL)
-		ga_append(&blob->bv_ga,
-			     (hex2nr(*bp) << 4) + hex2nr(*(bp+1)));
-	    if (bp[2] == '.' && vim_isxdigit(bp[3]))
-		++bp;
-	}
-	if (blob != NULL)
-	    rettv_blob_set(rettv, blob);
-	*arg = bp;
-    }
-    else
-    {
-	varnumber_T	n;
-
-	// decimal, hex or octal number
-	vim_str2nr(*arg, NULL, &len, current_sctx.sc_version >= 4
-		      ? STR2NR_NO_OCT + STR2NR_QUOTE
-		      : STR2NR_ALL, &n, NULL, 0, TRUE);
-	if (len == 0)
-	{
-	    semsg(_(e_invexpr2), *arg);
-	    return FAIL;
-	}
-	*arg += len;
-	if (evaluate)
-	{
-	    rettv->v_type = VAR_NUMBER;
-	    rettv->vval.v_number = n;
-	}
-    }
-    return OK;
-}
-
-/*
- * Allocate a variable for a string constant.
- * Return OK or FAIL.
- */
-    int
-get_string_tv(char_u **arg, typval_T *rettv, int evaluate)
-{
-    char_u	*p;
-    char_u	*name;
-    int		extra = 0;
-    int		len;
-
-    /*
-     * Find the end of the string, skipping backslashed characters.
-     */
-    for (p = *arg + 1; *p != NUL && *p != '"'; MB_PTR_ADV(p))
-    {
-	if (*p == '\\' && p[1] != NUL)
-	{
-	    ++p;
-	    // A "\<x>" form occupies at least 4 characters, and produces up
-	    // to 21 characters (3 * 6 for the char and 3 for a modifier):
-	    // reserve space for 18 extra.
-	    // Each byte in the char could be encoded as K_SPECIAL K_EXTRA x.
-	    if (*p == '<')
-		extra += 18;
-	}
-    }
-
-    if (*p != '"')
-    {
-	semsg(_("E114: Missing quote: %s"), *arg);
-	return FAIL;
-    }
-
-    // If only parsing, set *arg and return here
-    if (!evaluate)
-    {
-	*arg = p + 1;
-	return OK;
-    }
-
-    /*
-     * Copy the string into allocated memory, handling backslashed
-     * characters.
-     */
-    len = (int)(p - *arg + extra);
-    name = alloc(len);
-    if (name == NULL)
-	return FAIL;
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = name;
-
-    for (p = *arg + 1; *p != NUL && *p != '"'; )
-    {
-	if (*p == '\\')
-	{
-	    switch (*++p)
-	    {
-		case 'b': *name++ = BS; ++p; break;
-		case 'e': *name++ = ESC; ++p; break;
-		case 'f': *name++ = FF; ++p; break;
-		case 'n': *name++ = NL; ++p; break;
-		case 'r': *name++ = CAR; ++p; break;
-		case 't': *name++ = TAB; ++p; break;
-
-		case 'X': // hex: "\x1", "\x12"
-		case 'x':
-		case 'u': // Unicode: "\u0023"
-		case 'U':
-			  if (vim_isxdigit(p[1]))
-			  {
-			      int	n, nr;
-			      int	c = toupper(*p);
-
-			      if (c == 'X')
-				  n = 2;
-			      else if (*p == 'u')
-				  n = 4;
-			      else
-				  n = 8;
-			      nr = 0;
-			      while (--n >= 0 && vim_isxdigit(p[1]))
-			      {
-				  ++p;
-				  nr = (nr << 4) + hex2nr(*p);
-			      }
-			      ++p;
-			      // For "\u" store the number according to
-			      // 'encoding'.
-			      if (c != 'X')
-				  name += (*mb_char2bytes)(nr, name);
-			      else
-				  *name++ = nr;
-			  }
-			  break;
-
-			  // octal: "\1", "\12", "\123"
-		case '0':
-		case '1':
-		case '2':
-		case '3':
-		case '4':
-		case '5':
-		case '6':
-		case '7': *name = *p++ - '0';
-			  if (*p >= '0' && *p <= '7')
-			  {
-			      *name = (*name << 3) + *p++ - '0';
-			      if (*p >= '0' && *p <= '7')
-				  *name = (*name << 3) + *p++ - '0';
-			  }
-			  ++name;
-			  break;
-
-			    // Special key, e.g.: "\<C-W>"
-		case '<': extra = trans_special(&p, name, TRUE, TRUE,
-								   TRUE, NULL);
-			  if (extra != 0)
-			  {
-			      name += extra;
-			      if (name >= rettv->vval.v_string + len)
-				  iemsg("get_string_tv() used more space than allocated");
-			      break;
-			  }
-			  // FALLTHROUGH
-
-		default:  MB_COPY_CHAR(p, name);
-			  break;
-	    }
-	}
-	else
-	    MB_COPY_CHAR(p, name);
-
-    }
-    *name = NUL;
-    if (*p != NUL) // just in case
-	++p;
-    *arg = p;
-
-    return OK;
-}
-
-/*
- * Allocate a variable for a 'str''ing' constant.
- * Return OK or FAIL.
- */
-    int
-get_lit_string_tv(char_u **arg, typval_T *rettv, int evaluate)
-{
-    char_u	*p;
-    char_u	*str;
-    int		reduce = 0;
-
-    /*
-     * Find the end of the string, skipping ''.
-     */
-    for (p = *arg + 1; *p != NUL; MB_PTR_ADV(p))
-    {
-	if (*p == '\'')
-	{
-	    if (p[1] != '\'')
-		break;
-	    ++reduce;
-	    ++p;
-	}
-    }
-
-    if (*p != '\'')
-    {
-	semsg(_("E115: Missing quote: %s"), *arg);
-	return FAIL;
-    }
-
-    // If only parsing return after setting "*arg"
-    if (!evaluate)
-    {
-	*arg = p + 1;
-	return OK;
-    }
-
-    /*
-     * Copy the string into allocated memory, handling '' to ' reduction.
-     */
-    str = alloc((p - *arg) - reduce);
-    if (str == NULL)
-	return FAIL;
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = str;
-
-    for (p = *arg + 1; *p != NUL; )
-    {
-	if (*p == '\'')
-	{
-	    if (p[1] != '\'')
-		break;
-	    ++p;
-	}
-	MB_COPY_CHAR(p, str);
-    }
-    *str = NUL;
-    *arg = p + 1;
-
-    return OK;
-}
-
-/*
  * Return the function name of partial "pt".
  */
     char_u *
@@ -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.
@@ -4694,23 +4135,6 @@ echo_string(
 }
 
 /*
- * Return a string with the string representation of a variable.
- * If the memory is allocated "tofree" is set to it, otherwise NULL.
- * "numbuf" is used for a number.
- * Puts quotes around strings, so that they can be parsed back by eval().
- * May return NULL.
- */
-    char_u *
-tv2string(
-    typval_T	*tv,
-    char_u	**tofree,
-    char_u	*numbuf,
-    int		copyID)
-{
-    return echo_string_core(tv, tofree, numbuf, copyID, FALSE, TRUE, FALSE);
-}
-
-/*
  * Return string "str" in ' quotes, doubling ' characters.
  * If "str" is NULL an empty string is assumed.
  * If "function" is TRUE make it function('string').
@@ -4792,57 +4216,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.
  */
@@ -5424,558 +4797,6 @@ handle_subscript(
 }
 
 /*
- * Allocate memory for a variable type-value, and make it empty (0 or NULL
- * value).
- */
-    typval_T *
-alloc_tv(void)
-{
-    return ALLOC_CLEAR_ONE(typval_T);
-}
-
-/*
- * Allocate memory for a variable type-value, and assign a string to it.
- * The string "s" must have been allocated, it is consumed.
- * Return NULL for out of memory, the variable otherwise.
- */
-    typval_T *
-alloc_string_tv(char_u *s)
-{
-    typval_T	*rettv;
-
-    rettv = alloc_tv();
-    if (rettv != NULL)
-    {
-	rettv->v_type = VAR_STRING;
-	rettv->vval.v_string = s;
-    }
-    else
-	vim_free(s);
-    return rettv;
-}
-
-/*
- * Free the memory for a variable type-value.
- */
-    void
-free_tv(typval_T *varp)
-{
-    if (varp != NULL)
-    {
-	switch (varp->v_type)
-	{
-	    case VAR_FUNC:
-		func_unref(varp->vval.v_string);
-		// FALLTHROUGH
-	    case VAR_STRING:
-		vim_free(varp->vval.v_string);
-		break;
-	    case VAR_PARTIAL:
-		partial_unref(varp->vval.v_partial);
-		break;
-	    case VAR_BLOB:
-		blob_unref(varp->vval.v_blob);
-		break;
-	    case VAR_LIST:
-		list_unref(varp->vval.v_list);
-		break;
-	    case VAR_DICT:
-		dict_unref(varp->vval.v_dict);
-		break;
-	    case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
-		job_unref(varp->vval.v_job);
-		break;
-#endif
-	    case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
-		channel_unref(varp->vval.v_channel);
-		break;
-#endif
-	    case VAR_NUMBER:
-	    case VAR_FLOAT:
-	    case VAR_ANY:
-	    case VAR_UNKNOWN:
-	    case VAR_VOID:
-	    case VAR_BOOL:
-	    case VAR_SPECIAL:
-		break;
-	}
-	vim_free(varp);
-    }
-}
-
-/*
- * Free the memory for a variable value and set the value to NULL or 0.
- */
-    void
-clear_tv(typval_T *varp)
-{
-    if (varp != NULL)
-    {
-	switch (varp->v_type)
-	{
-	    case VAR_FUNC:
-		func_unref(varp->vval.v_string);
-		// FALLTHROUGH
-	    case VAR_STRING:
-		VIM_CLEAR(varp->vval.v_string);
-		break;
-	    case VAR_PARTIAL:
-		partial_unref(varp->vval.v_partial);
-		varp->vval.v_partial = NULL;
-		break;
-	    case VAR_BLOB:
-		blob_unref(varp->vval.v_blob);
-		varp->vval.v_blob = NULL;
-		break;
-	    case VAR_LIST:
-		list_unref(varp->vval.v_list);
-		varp->vval.v_list = NULL;
-		break;
-	    case VAR_DICT:
-		dict_unref(varp->vval.v_dict);
-		varp->vval.v_dict = NULL;
-		break;
-	    case VAR_NUMBER:
-	    case VAR_BOOL:
-	    case VAR_SPECIAL:
-		varp->vval.v_number = 0;
-		break;
-	    case VAR_FLOAT:
-#ifdef FEAT_FLOAT
-		varp->vval.v_float = 0.0;
-		break;
-#endif
-	    case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
-		job_unref(varp->vval.v_job);
-		varp->vval.v_job = NULL;
-#endif
-		break;
-	    case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
-		channel_unref(varp->vval.v_channel);
-		varp->vval.v_channel = NULL;
-#endif
-	    case VAR_UNKNOWN:
-	    case VAR_ANY:
-	    case VAR_VOID:
-		break;
-	}
-	varp->v_lock = 0;
-    }
-}
-
-/*
- * Set the value of a variable to NULL without freeing items.
- */
-    void
-init_tv(typval_T *varp)
-{
-    if (varp != NULL)
-	CLEAR_POINTER(varp);
-}
-
-/*
- * Get the number value of a variable.
- * If it is a String variable, uses vim_str2nr().
- * For incompatible types, return 0.
- * tv_get_number_chk() is similar to tv_get_number(), but informs the
- * caller of incompatible types: it sets *denote to TRUE if "denote"
- * is not NULL or returns -1 otherwise.
- */
-    varnumber_T
-tv_get_number(typval_T *varp)
-{
-    int		error = FALSE;
-
-    return tv_get_number_chk(varp, &error);	// return 0L on error
-}
-
-    varnumber_T
-tv_get_number_chk(typval_T *varp, int *denote)
-{
-    varnumber_T	n = 0L;
-
-    switch (varp->v_type)
-    {
-	case VAR_NUMBER:
-	    return varp->vval.v_number;
-	case VAR_FLOAT:
-#ifdef FEAT_FLOAT
-	    emsg(_("E805: Using a Float as a Number"));
-	    break;
-#endif
-	case VAR_FUNC:
-	case VAR_PARTIAL:
-	    emsg(_("E703: Using a Funcref as a Number"));
-	    break;
-	case VAR_STRING:
-	    if (varp->vval.v_string != NULL)
-		vim_str2nr(varp->vval.v_string, NULL, NULL,
-					    STR2NR_ALL, &n, NULL, 0, FALSE);
-	    return n;
-	case VAR_LIST:
-	    emsg(_("E745: Using a List as a Number"));
-	    break;
-	case VAR_DICT:
-	    emsg(_("E728: Using a Dictionary as a Number"));
-	    break;
-	case VAR_BOOL:
-	case VAR_SPECIAL:
-	    return varp->vval.v_number == VVAL_TRUE ? 1 : 0;
-	case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
-	    emsg(_("E910: Using a Job as a Number"));
-	    break;
-#endif
-	case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
-	    emsg(_("E913: Using a Channel as a Number"));
-	    break;
-#endif
-	case VAR_BLOB:
-	    emsg(_("E974: Using a Blob as a Number"));
-	    break;
-	case VAR_UNKNOWN:
-	case VAR_ANY:
-	case VAR_VOID:
-	    internal_error_no_abort("tv_get_number(UNKNOWN)");
-	    break;
-    }
-    if (denote == NULL)		// useful for values that must be unsigned
-	n = -1;
-    else
-	*denote = TRUE;
-    return n;
-}
-
-#ifdef FEAT_FLOAT
-    float_T
-tv_get_float(typval_T *varp)
-{
-    switch (varp->v_type)
-    {
-	case VAR_NUMBER:
-	    return (float_T)(varp->vval.v_number);
-	case VAR_FLOAT:
-	    return varp->vval.v_float;
-	case VAR_FUNC:
-	case VAR_PARTIAL:
-	    emsg(_("E891: Using a Funcref as a Float"));
-	    break;
-	case VAR_STRING:
-	    emsg(_("E892: Using a String as a Float"));
-	    break;
-	case VAR_LIST:
-	    emsg(_("E893: Using a List as a Float"));
-	    break;
-	case VAR_DICT:
-	    emsg(_("E894: Using a Dictionary as a Float"));
-	    break;
-	case VAR_BOOL:
-	    emsg(_("E362: Using a boolean value as a Float"));
-	    break;
-	case VAR_SPECIAL:
-	    emsg(_("E907: Using a special value as a Float"));
-	    break;
-	case VAR_JOB:
-# ifdef FEAT_JOB_CHANNEL
-	    emsg(_("E911: Using a Job as a Float"));
-	    break;
-# endif
-	case VAR_CHANNEL:
-# ifdef FEAT_JOB_CHANNEL
-	    emsg(_("E914: Using a Channel as a Float"));
-	    break;
-# endif
-	case VAR_BLOB:
-	    emsg(_("E975: Using a Blob as a Float"));
-	    break;
-	case VAR_UNKNOWN:
-	case VAR_ANY:
-	case VAR_VOID:
-	    internal_error_no_abort("tv_get_float(UNKNOWN)");
-	    break;
-    }
-    return 0;
-}
-#endif
-
-/*
- * Get the string value of a variable.
- * If it is a Number variable, the number is converted into a string.
- * tv_get_string() uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
- * tv_get_string_buf() uses a given buffer.
- * If the String variable has never been set, return an empty string.
- * Never returns NULL;
- * tv_get_string_chk() and tv_get_string_buf_chk() are similar, but return
- * NULL on error.
- */
-    char_u *
-tv_get_string(typval_T *varp)
-{
-    static char_u   mybuf[NUMBUFLEN];
-
-    return tv_get_string_buf(varp, mybuf);
-}
-
-    char_u *
-tv_get_string_buf(typval_T *varp, char_u *buf)
-{
-    char_u	*res =  tv_get_string_buf_chk(varp, buf);
-
-    return res != NULL ? res : (char_u *)"";
-}
-
-/*
- * Careful: This uses a single, static buffer.  YOU CAN ONLY USE IT ONCE!
- */
-    char_u *
-tv_get_string_chk(typval_T *varp)
-{
-    static char_u   mybuf[NUMBUFLEN];
-
-    return tv_get_string_buf_chk(varp, mybuf);
-}
-
-    char_u *
-tv_get_string_buf_chk(typval_T *varp, char_u *buf)
-{
-    switch (varp->v_type)
-    {
-	case VAR_NUMBER:
-	    vim_snprintf((char *)buf, NUMBUFLEN, "%lld",
-					    (varnumber_T)varp->vval.v_number);
-	    return buf;
-	case VAR_FUNC:
-	case VAR_PARTIAL:
-	    emsg(_("E729: using Funcref as a String"));
-	    break;
-	case VAR_LIST:
-	    emsg(_("E730: using List as a String"));
-	    break;
-	case VAR_DICT:
-	    emsg(_("E731: using Dictionary as a String"));
-	    break;
-	case VAR_FLOAT:
-#ifdef FEAT_FLOAT
-	    emsg(_(e_float_as_string));
-	    break;
-#endif
-	case VAR_STRING:
-	    if (varp->vval.v_string != NULL)
-		return varp->vval.v_string;
-	    return (char_u *)"";
-	case VAR_BOOL:
-	case VAR_SPECIAL:
-	    STRCPY(buf, get_var_special_name(varp->vval.v_number));
-	    return buf;
-        case VAR_BLOB:
-	    emsg(_("E976: using Blob as a String"));
-	    break;
-	case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
-	    {
-		job_T *job = varp->vval.v_job;
-		char  *status;
-
-		if (job == NULL)
-		    return (char_u *)"no process";
-		status = job->jv_status == JOB_FAILED ? "fail"
-				: job->jv_status >= JOB_ENDED ? "dead"
-				: "run";
-# ifdef UNIX
-		vim_snprintf((char *)buf, NUMBUFLEN,
-			    "process %ld %s", (long)job->jv_pid, status);
-# elif defined(MSWIN)
-		vim_snprintf((char *)buf, NUMBUFLEN,
-			    "process %ld %s",
-			    (long)job->jv_proc_info.dwProcessId,
-			    status);
-# else
-		// fall-back
-		vim_snprintf((char *)buf, NUMBUFLEN, "process ? %s", status);
-# endif
-		return buf;
-	    }
-#endif
-	    break;
-	case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
-	    {
-		channel_T *channel = varp->vval.v_channel;
-		char      *status = channel_status(channel, -1);
-
-		if (channel == NULL)
-		    vim_snprintf((char *)buf, NUMBUFLEN, "channel %s", status);
-		else
-		    vim_snprintf((char *)buf, NUMBUFLEN,
-				     "channel %d %s", channel->ch_id, status);
-		return buf;
-	    }
-#endif
-	    break;
-	case VAR_UNKNOWN:
-	case VAR_ANY:
-	case VAR_VOID:
-	    emsg(_(e_inval_string));
-	    break;
-    }
-    return NULL;
-}
-
-/*
- * Turn a typeval into a string.  Similar to tv_get_string_buf() but uses
- * string() on Dict, List, etc.
- */
-    static char_u *
-tv_stringify(typval_T *varp, char_u *buf)
-{
-    if (varp->v_type == VAR_LIST
-	    || varp->v_type == VAR_DICT
-	    || varp->v_type == VAR_BLOB
-	    || varp->v_type == VAR_FUNC
-	    || varp->v_type == VAR_PARTIAL
-	    || varp->v_type == VAR_FLOAT)
-    {
-	typval_T tmp;
-
-	f_string(varp, &tmp);
-	tv_get_string_buf(&tmp, buf);
-	clear_tv(varp);
-	*varp = tmp;
-	return tmp.vval.v_string;
-    }
-    return tv_get_string_buf(varp, buf);
-}
-
-/*
- * Return TRUE if typeval "tv" and its value are set to be locked (immutable).
- * Also give an error message, using "name" or _("name") when use_gettext is
- * TRUE.
- */
-    static int
-tv_check_lock(typval_T *tv, char_u *name, int use_gettext)
-{
-    int	lock = 0;
-
-    switch (tv->v_type)
-    {
-	case VAR_BLOB:
-	    if (tv->vval.v_blob != NULL)
-		lock = tv->vval.v_blob->bv_lock;
-	    break;
-	case VAR_LIST:
-	    if (tv->vval.v_list != NULL)
-		lock = tv->vval.v_list->lv_lock;
-	    break;
-	case VAR_DICT:
-	    if (tv->vval.v_dict != NULL)
-		lock = tv->vval.v_dict->dv_lock;
-	    break;
-	default:
-	    break;
-    }
-    return var_check_lock(tv->v_lock, name, use_gettext)
-		    || (lock != 0 && var_check_lock(lock, name, use_gettext));
-}
-
-/*
- * Copy the values from typval_T "from" to typval_T "to".
- * When needed allocates string or increases reference count.
- * Does not make a copy of a list, blob or dict but copies the reference!
- * It is OK for "from" and "to" to point to the same item.  This is used to
- * make a copy later.
- */
-    void
-copy_tv(typval_T *from, typval_T *to)
-{
-    to->v_type = from->v_type;
-    to->v_lock = 0;
-    switch (from->v_type)
-    {
-	case VAR_NUMBER:
-	case VAR_BOOL:
-	case VAR_SPECIAL:
-	    to->vval.v_number = from->vval.v_number;
-	    break;
-	case VAR_FLOAT:
-#ifdef FEAT_FLOAT
-	    to->vval.v_float = from->vval.v_float;
-	    break;
-#endif
-	case VAR_JOB:
-#ifdef FEAT_JOB_CHANNEL
-	    to->vval.v_job = from->vval.v_job;
-	    if (to->vval.v_job != NULL)
-		++to->vval.v_job->jv_refcount;
-	    break;
-#endif
-	case VAR_CHANNEL:
-#ifdef FEAT_JOB_CHANNEL
-	    to->vval.v_channel = from->vval.v_channel;
-	    if (to->vval.v_channel != NULL)
-		++to->vval.v_channel->ch_refcount;
-	    break;
-#endif
-	case VAR_STRING:
-	case VAR_FUNC:
-	    if (from->vval.v_string == NULL)
-		to->vval.v_string = NULL;
-	    else
-	    {
-		to->vval.v_string = vim_strsave(from->vval.v_string);
-		if (from->v_type == VAR_FUNC)
-		    func_ref(to->vval.v_string);
-	    }
-	    break;
-	case VAR_PARTIAL:
-	    if (from->vval.v_partial == NULL)
-		to->vval.v_partial = NULL;
-	    else
-	    {
-		to->vval.v_partial = from->vval.v_partial;
-		++to->vval.v_partial->pt_refcount;
-	    }
-	    break;
-	case VAR_BLOB:
-	    if (from->vval.v_blob == NULL)
-		to->vval.v_blob = NULL;
-	    else
-	    {
-		to->vval.v_blob = from->vval.v_blob;
-		++to->vval.v_blob->bv_refcount;
-	    }
-	    break;
-	case VAR_LIST:
-	    if (from->vval.v_list == NULL)
-		to->vval.v_list = NULL;
-	    else
-	    {
-		to->vval.v_list = from->vval.v_list;
-		++to->vval.v_list->lv_refcount;
-	    }
-	    break;
-	case VAR_DICT:
-	    if (from->vval.v_dict == NULL)
-		to->vval.v_dict = NULL;
-	    else
-	    {
-		to->vval.v_dict = from->vval.v_dict;
-		++to->vval.v_dict->dv_refcount;
-	    }
-	    break;
-	case VAR_UNKNOWN:
-	case VAR_ANY:
-	case VAR_VOID:
-	    internal_error_no_abort("copy_tv(UNKNOWN)");
-	    break;
-    }
-}
-
-/*
  * Make a copy of an item.
  * Lists and Dictionaries are also copied.  A deep copy if "deep" is set.
  * For deepcopy() "copyID" is zero for a full copy or the ID for when a
@@ -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
 
 /*
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -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".
@@ -1501,33 +1463,6 @@ f_balloon_split(typval_T *argvars, typva
 #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.
  */
@@ -5106,22 +5041,6 @@ f_invert(typval_T *argvars, typval_T *re
 }
 
 /*
- * Return TRUE if typeval "tv" is locked: Either that value is locked itself
- * or it refers to a List or Dictionary that is locked.
- */
-    static int
-tv_islocked(typval_T *tv)
-{
-    return (tv->v_lock & VAR_LOCKED)
-	|| (tv->v_type == VAR_LIST
-		&& tv->vval.v_list != NULL
-		&& (tv->vval.v_list->lv_lock & VAR_LOCKED))
-	|| (tv->v_type == VAR_DICT
-		&& tv->vval.v_dict != NULL
-		&& (tv->vval.v_dict->dv_lock & VAR_LOCKED));
-}
-
-/*
  * "islocked()" function
  */
     static void
--- a/src/globals.h
+++ b/src/globals.h
@@ -1748,6 +1748,9 @@ EXTERN char e_notset[]	INIT(= N_("E764: 
 #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
--- a/src/proto.h
+++ b/src/proto.h
@@ -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"
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -29,13 +29,8 @@ int eval0(char_u *arg, typval_T *rettv, 
 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
 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_
 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 : */
--- a/src/proto/evalfunc.pro
+++ b/src/proto/evalfunc.pro
@@ -10,9 +10,6 @@ int call_internal_func(char_u *name, int
 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);
new file mode 100644
--- /dev/null
+++ b/src/proto/typval.pro
@@ -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 : */
new file mode 100644
--- /dev/null
+++ b/src/typval.c
@@ -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
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    847,
+/**/
     846,
 /**/
     845,