changeset 7712:bce3b5ddb393 v7.4.1154

commit https://github.com/vim/vim/commit/520e1e41f35b063ede63b41738c82d6636e78c34 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Jan 23 19:46:28 2016 +0100 patch 7.4.1154 Problem: No support for JSON. Solution: Add jsonencode() and jsondecode(). Also add v:false, v:true, v:null and v:none.
author Christian Brabandt <cb@256bit.org>
date Sat, 23 Jan 2016 20:00:04 +0100
parents 5fe266fb2a6e
children 1324d6282344
files Filelist runtime/doc/eval.txt src/Make_bc3.mak src/Make_bc5.mak src/Make_cyg_ming.mak src/Make_dice.mak src/Make_ivc.mak src/Make_manx.mak src/Make_morph.mak src/Make_mvc.mak src/Make_sas.mak src/Make_vms.mms src/Makefile src/eval.c src/globals.h src/if_lua.c src/if_mzsch.c src/if_py_both.h src/if_ruby.c src/json.c src/proto.h src/proto/eval.pro src/proto/json.pro src/structs.h src/testdir/test_alot.vim src/testdir/test_json.vim src/version.c src/vim.h
diffstat 28 files changed, 863 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/Filelist
+++ b/Filelist
@@ -40,6 +40,7 @@ SRC_ALL =	\
 		src/hardcopy.c \
 		src/hashtab.c \
 		src/keymap.h \
+		src/json.c \
 		src/macros.h \
 		src/main.c \
 		src/mark.c \
@@ -132,6 +133,7 @@ SRC_ALL =	\
 		src/proto/gui_beval.pro \
 		src/proto/hardcopy.pro \
 		src/proto/hashtab.pro \
+		src/proto/json.pro \
 		src/proto/main.pro \
 		src/proto/mark.pro \
 		src/proto/mbyte.pro \
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1,4 +1,4 @@
-*eval.txt*	For Vim version 7.4.  Last change: 2016 Jan 21
+*eval.txt*	For Vim version 7.4.  Last change: 2016 Jan 23
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -1409,6 +1409,10 @@ v:exception	The value of the exception m
 	:endtry
 <		Output: "caught oops".
 
+					*v:false* *false-variable*
+v:false		A Number with value zero. Used to put "false" in JSON.  See
+		|jsonencode()|.
+
 					*v:fcs_reason* *fcs_reason-variable*
 v:fcs_reason	The reason why the |FileChangedShell| event was triggered.
 		Can be used in an autocommand to decide what to do and/or what
@@ -1542,6 +1546,14 @@ v:mouse_col	Column number for a mouse cl
 		This is the screen column number, like with |virtcol()|.  The
 		value is zero when there was no mouse button click.
 
+					*v:none* *none-variable*
+v:none		An empty String. Used to put an empty item in JSON.  See
+		|jsonencode()|.
+
+					*v:null* *null-variable*
+v:null		An empty String. Used to put "null" in JSON.  See
+		|jsonencode()|.
+
 					*v:oldfiles* *oldfiles-variable*
 v:oldfiles	List of file names that is loaded from the |viminfo| file on
 		startup.  These are the files that Vim remembers marks for.
@@ -1707,6 +1719,10 @@ v:throwpoint	The point where the excepti
 	:endtry
 <		Output: "Exception from test.vim, line 2"
 
+						*v:true* *true-variable*
+v:true		A Number with value one. Used to put "true" in JSON.  See
+		|jsonencode()|.
+
 						*v:val* *val-variable*
 v:val		Value of the current item of a |List| or |Dictionary|.	Only
 		valid while evaluating the expression used with |map()| and
@@ -1913,6 +1929,8 @@ isdirectory( {directory})	Number	TRUE if
 islocked( {expr})		Number	TRUE if {expr} is locked
 items( {dict})			List	key-value pairs in {dict}
 join( {list} [, {sep}])		String	join {list} items into one String
+jsondecode( {string})		any	decode JSON
+jsonencode( {expr})		String	encode JSON
 keys( {dict})			List	keys in {dict}
 len( {expr})			Number	the length of {expr}
 libcall( {lib}, {func}, {arg})	String	call {func} in library {lib} with {arg}
@@ -4215,6 +4233,27 @@ join({list} [, {sep}])					*join()*
 		converted into a string like with |string()|.
 		The opposite function is |split()|.
 
+jsondecode({string})					*jsondecode()*
+		TODO
+
+jsonencode({expr})					*jsonencode()*
+		Encodode {expr} as JSON and return this as a string.
+		The encoding is specified in:
+		http://www.ietf.org/rfc/rfc4627.txt
+		Vim values are converted as follows:
+		   Number		decimal number
+		   Float		floating point number
+		   String		in double quotes (possibly null)
+		   Funcref		nothing
+		   List			as an array (possibly null); when
+		   			used recursively: []
+		   Dict			as an object (possibly null); when
+		   			used recursively: {}
+		   v:false		"false"
+		   v:true		"true"
+		   v:none		nothing
+		   v:null		"null"
+
 keys({dict})						*keys()*
 		Return a |List| with all the keys of {dict}.  The |List| is in
 		arbitrary order.
--- a/src/Make_bc3.mak
+++ b/src/Make_bc3.mak
@@ -72,6 +72,7 @@ EXE_dependencies = \
 	getchar.obj \
 	hardcopy.obj \
 	hashtab.obj \
+	json.obj \
 	main.obj \
 	mark.obj \
 	memfile.obj \
--- a/src/Make_bc5.mak
+++ b/src/Make_bc5.mak
@@ -598,6 +598,7 @@ vimobj =  \
 	$(OBJDIR)\getchar.obj \
 	$(OBJDIR)\hardcopy.obj \
 	$(OBJDIR)\hashtab.obj \
+	$(OBJDIR)\json.obj \
 	$(OBJDIR)\main.obj \
 	$(OBJDIR)\mark.obj \
 	$(OBJDIR)\memfile.obj \
--- a/src/Make_cyg_ming.mak
+++ b/src/Make_cyg_ming.mak
@@ -601,6 +601,7 @@ OBJ = \
 	$(OUTDIR)/getchar.o \
 	$(OUTDIR)/hardcopy.o \
 	$(OUTDIR)/hashtab.o \
+	$(OUTDIR)/json.o \
 	$(OUTDIR)/main.o \
 	$(OUTDIR)/mark.o \
 	$(OUTDIR)/memfile.o \
--- a/src/Make_dice.mak
+++ b/src/Make_dice.mak
@@ -45,6 +45,7 @@ SRC = \
 	getchar.c \
 	hardcopy.c \
 	hashtab.c \
+	json.c \
 	main.c \
 	mark.c \
 	memfile.c \
@@ -93,6 +94,7 @@ OBJ =	o/blowfish.o \
 	o/getchar.o \
 	o/hardcopy.o \
 	o/hashtab.o \
+	o/json.o \
 	o/main.o \
 	o/mark.o \
 	o/memfile.o \
@@ -179,6 +181,8 @@ o/hardcopy.o: hardcopy.c	$(SYMS)
 
 o/hashtab.o: hashtab.c	$(SYMS)
 
+o/json.o:	json.c  $(SYMS)
+
 o/main.o: main.c $(SYMS)
 
 o/mark.o: mark.c	$(SYMS)
--- a/src/Make_ivc.mak
+++ b/src/Make_ivc.mak
@@ -229,6 +229,7 @@ LINK32_OBJS= \
 	"$(INTDIR)/getchar.obj" \
 	"$(INTDIR)/hardcopy.obj" \
 	"$(INTDIR)/hashtab.obj" \
+	"$(INTDIR)/json.obj" \
 	"$(INTDIR)/main.obj" \
 	"$(INTDIR)/mark.obj" \
 	"$(INTDIR)/mbyte.obj" \
@@ -555,6 +556,10 @@ SOURCE=.\if_ole.idl
 # End Source File
 # Begin Source File
 
+SOURCE=.\json.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\main.c
 # End Source File
 # Begin Source File
--- a/src/Make_manx.mak
+++ b/src/Make_manx.mak
@@ -55,6 +55,7 @@ SRC =	blowfish.c \
 	getchar.c \
 	hardcopy.c \
 	hashtab.c \
+	json.c \
 	main.c \
 	mark.c \
 	memfile.c \
@@ -105,6 +106,7 @@ OBJ =	obj/blowfish.o \
 	obj/getchar.o \
 	obj/hardcopy.o \
 	obj/hashtab.o \
+	obj/json.o \
 	obj/main.o \
 	obj/mark.o \
 	obj/memfile.o \
@@ -153,6 +155,7 @@ PRO =	proto/blowfish.pro \
 	proto/getchar.pro \
 	proto/hardcopy.pro \
 	proto/hashtab.pro \
+	proto/json.pro \
 	proto/main.pro \
 	proto/mark.pro \
 	proto/memfile.pro \
@@ -284,6 +287,9 @@ obj/hardcopy.o:	hardcopy.c
 obj/hashtab.o:	hashtab.c
 	$(CCSYM) $@ hashtab.c
 
+obj/json.o:	json.c
+	$(CCSYM) $@ json.c
+
 # Don't use $(SYMS) here, because main.c defines EXTERN
 obj/main.o:	main.c option.h globals.h
 	$(CCNOSYM) $@ main.c
--- a/src/Make_morph.mak
+++ b/src/Make_morph.mak
@@ -43,6 +43,7 @@ SRC =	blowfish.c						\
 	getchar.c						\
 	hardcopy.c						\
 	hashtab.c						\
+	json.c							\
 	main.c							\
 	mark.c							\
 	mbyte.c							\
--- a/src/Make_mvc.mak
+++ b/src/Make_mvc.mak
@@ -536,6 +536,7 @@ OBJ = \
 	$(OUTDIR)\getchar.obj \
 	$(OUTDIR)\hardcopy.obj \
 	$(OUTDIR)\hashtab.obj \
+	$(OUTDIR)\json.obj \
 	$(OUTDIR)\main.obj \
 	$(OUTDIR)\mark.obj \
 	$(OUTDIR)\mbyte.obj \
@@ -1202,6 +1203,8 @@ lib$(MZSCHEME_MAIN_LIB)$(MZSCHEME_VER).l
 $(OUTDIR)/if_tcl.obj: $(OUTDIR) if_tcl.c  $(INCL)
 	$(CC) $(CFLAGS) $(TCL_INC) if_tcl.c
 
+$(OUTDIR)/json.obj:	$(OUTDIR) json.c  $(INCL)
+
 $(OUTDIR)/main.obj:	$(OUTDIR) main.c  $(INCL)
 
 $(OUTDIR)/mark.obj:	$(OUTDIR) mark.c  $(INCL)
@@ -1329,6 +1332,7 @@ proto.h: \
 	proto/getchar.pro \
 	proto/hardcopy.pro \
 	proto/hashtab.pro \
+	proto/json.pro \
 	proto/main.pro \
 	proto/mark.pro \
 	proto/memfile.pro \
--- a/src/Make_sas.mak
+++ b/src/Make_sas.mak
@@ -108,6 +108,7 @@ SRC = \
 	getchar.c \
 	hardcopy.c \
 	hashtab.c \
+	json.c \
 	main.c \
 	mark.c \
 	memfile.c \
@@ -157,6 +158,7 @@ OBJ = \
 	getchar.o \
 	hardcopy.o \
 	hashtab.o \
+	json.o \
 	main.o \
 	mark.o \
 	memfile.o \
@@ -206,6 +208,7 @@ PRO = \
 	proto/getchar.pro \
 	proto/hardcopy.pro \
 	proto/hashtab.pro \
+	proto/json.pro \
 	proto/main.pro \
 	proto/mark.pro \
 	proto/memfile.pro \
@@ -328,6 +331,8 @@ hardcopy.o:		hardcopy.c
 proto/hardcopy.pro:	hardcopy.c
 hashtab.o:		hashtab.c
 proto/hashtab.pro:	hashtab.c
+json.o:			json.c
+proto/json.pro:		json.c
 main.o:			main.c
 proto/main.pro:		main.c
 mark.o:			mark.c
--- a/src/Make_vms.mms
+++ b/src/Make_vms.mms
@@ -2,7 +2,7 @@
 # Makefile for Vim on OpenVMS
 #
 # Maintainer:   Zoltan Arpadffy <arpadffy@polarhome.com>
-# Last change:  2014 Aug 10
+# Last change:  2016 Jan 22
 #
 # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64
 # with MMS and MMK
@@ -311,7 +311,7 @@ ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_
 
 SRC =	blowfish.c buffer.c charset.c crypt.c, crypt_zip.c diff.c digraph.c edit.c eval.c ex_cmds.c ex_cmds2.c \
 	ex_docmd.c ex_eval.c ex_getln.c if_xcmdsrv.c fileio.c fold.c getchar.c \
-	hardcopy.c hashtab.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \
+	hardcopy.c hashtab.c json.c main.c mark.c menu.c mbyte.c memfile.c memline.c message.c misc1.c \
 	misc2.c move.c normal.c ops.c option.c popupmnu.c quickfix.c regexp.c search.c sha256.c\
 	spell.c syntax.c tag.c term.c termlib.c ui.c undo.c version.c screen.c \
 	window.c os_unix.c os_vms.c pathdef.c \
@@ -320,7 +320,7 @@ SRC =	blowfish.c buffer.c charset.c cryp
 
 OBJ =	blowfish.obj buffer.obj charset.obj crypt.obj, crypt_zip.obj diff.obj digraph.obj edit.obj eval.obj \
 	ex_cmds.obj ex_cmds2.obj ex_docmd.obj ex_eval.obj ex_getln.obj \
-	if_xcmdsrv.obj fileio.obj fold.obj getchar.obj hardcopy.obj hashtab.obj main.obj mark.obj \
+	if_xcmdsrv.obj fileio.obj fold.obj getchar.obj hardcopy.obj hashtab.obj json.obj main.obj mark.obj \
 	menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \
 	move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj quickfix.obj \
 	regexp.obj search.obj sha256.obj spell.obj syntax.obj tag.obj term.obj termlib.obj \
@@ -572,6 +572,10 @@ if_mzsch.obj : if_mzsch.c vim.h [.auto]c
  ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h [.proto]gui_beval.pro ex_cmds.h proto.h \
  globals.h farsi.h arabic.h if_mzsch.h 
+json.obj : json.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 gui_beval.h \
+ [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \
+ arabic.h version.h
 main.obj : main.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 gui_beval.h \
  [.proto]gui_beval.pro option.h ex_cmds.h proto.h globals.h farsi.h \
--- a/src/Makefile
+++ b/src/Makefile
@@ -1487,6 +1487,7 @@ BASIC_SRC = \
 	hashtab.c \
 	if_cscope.c \
 	if_xcmdsrv.c \
+	json.c \
 	main.c \
 	mark.c \
 	memfile.c \
@@ -1580,6 +1581,7 @@ OBJ_COMMON = \
 	$(HANGULIN_OBJ) \
 	objects/if_cscope.o \
 	objects/if_xcmdsrv.o \
+	objects/json.o \
 	objects/mark.o \
         objects/memline.o \
 	objects/menu.o \
@@ -1654,6 +1656,7 @@ PRO_AUTO = \
 	if_python.pro \
 	if_python3.pro \
 	if_ruby.pro \
+	json.pro \
 	main.pro \
 	mark.pro \
 	memfile.pro \
@@ -1986,6 +1989,8 @@ test_arglist \
 	test_expand \
 	test_hardcopy \
 	test_increment \
+	test_json \
+	test_langmap \
 	test_lispwords \
 	test_menu \
 	test_perl \
@@ -1993,6 +1998,7 @@ test_arglist \
 	test_searchpos \
 	test_set \
 	test_sort \
+	test_syntax \
 	test_undolevels \
 	test_unlet \
 	test_viminfo \
@@ -2770,6 +2776,9 @@ objects/if_tcl.o: if_tcl.c
 objects/integration.o: integration.c
 	$(CCC) -o $@ integration.c
 
+objects/json.o: json.c
+	$(CCC) -o $@ json.c
+
 objects/main.o: main.c
 	$(CCC) -o $@ main.c
 
@@ -3060,6 +3069,10 @@ objects/if_xcmdsrv.o: if_xcmdsrv.c vim.h
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
  globals.h farsi.h arabic.h version.h
+objects/json.o: json.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
+ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
+ gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
+ arabic.h
 objects/main.o: main.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
  ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
  gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
@@ -3114,7 +3127,7 @@ objects/option.o: option.c vim.h auto/co
 objects/os_unix.o: os_unix.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
  ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
  gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
- arabic.h if_mzsch.h os_unixx.h
+ arabic.h os_unixx.h
 objects/pathdef.o: auto/pathdef.c vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
@@ -3179,7 +3192,7 @@ objects/gui.o: gui.c vim.h auto/config.h
 objects/gui_gtk.o: gui_gtk.c gui_gtk_f.h vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
- globals.h farsi.h arabic.h ../pixmaps/stock_icons.h
+ globals.h farsi.h arabic.h
 objects/gui_gtk_f.o: gui_gtk_f.c vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
@@ -3245,8 +3258,8 @@ objects/gui_athena.o: gui_athena.c vim.h
 objects/gui_gtk_x11.o: gui_gtk_x11.c vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
- globals.h farsi.h arabic.h gui_gtk_f.h ../runtime/vim32x32.xpm \
- ../runtime/vim16x16.xpm ../runtime/vim48x48.xpm $(GRESOURCE_HDR)
+ globals.h farsi.h arabic.h auto/gui_gtk_gresources.h gui_gtk_f.h \
+ ../runtime/vim32x32.xpm ../runtime/vim16x16.xpm ../runtime/vim48x48.xpm
 objects/gui_x11.o: gui_x11.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \
  ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \
  gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h globals.h farsi.h \
@@ -3278,7 +3291,7 @@ objects/if_lua.o: if_lua.c vim.h auto/co
 objects/if_mzsch.o: if_mzsch.c vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
- globals.h farsi.h arabic.h if_mzsch.h mzscheme_base.c
+ globals.h farsi.h arabic.h if_mzsch.h
 objects/if_perl.o: auto/if_perl.c vim.h auto/config.h feature.h os_unix.h \
  auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \
  regexp.h gui.h gui_beval.h proto/gui_beval.pro ex_cmds.h proto.h \
--- a/src/eval.c
+++ b/src/eval.c
@@ -99,7 +99,6 @@ static char *e_undefvar = N_("E121: Unde
 static char *e_missbrac = N_("E111: Missing ']'");
 static char *e_listarg = N_("E686: Argument of %s must be a List");
 static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionary");
-static char *e_emptykey = N_("E713: Cannot use empty key for Dictionary");
 static char *e_listreq = N_("E714: List required");
 static char *e_dictreq = N_("E715: Dictionary required");
 static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
@@ -371,6 +370,10 @@ static struct vimvar
     {VV_NAME("option_old",	 VAR_STRING), VV_RO},
     {VV_NAME("option_type",	 VAR_STRING), VV_RO},
     {VV_NAME("errors",		 VAR_LIST), 0},
+    {VV_NAME("false",		 VAR_SPECIAL), VV_RO},
+    {VV_NAME("true",		 VAR_SPECIAL), VV_RO},
+    {VV_NAME("null",		 VAR_SPECIAL), VV_RO},
+    {VV_NAME("none",		 VAR_SPECIAL), VV_RO},
 };
 
 /* shorthand */
@@ -428,7 +431,6 @@ static int get_option_tv __ARGS((char_u 
 static int get_string_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
 static int get_lit_string_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
 static int get_list_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
-static int rettv_list_alloc __ARGS((typval_T *rettv));
 static long list_len __ARGS((list_T *l));
 static int list_equal __ARGS((list_T *l1, list_T *l2, int ic, int recursive));
 static int dict_equal __ARGS((dict_T *d1, dict_T *d2, int ic, int recursive));
@@ -443,7 +445,6 @@ static char_u *list2string __ARGS((typva
 static int list_join_inner __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo_style, int copyID, garray_T *join_gap));
 static int list_join __ARGS((garray_T *gap, list_T *l, char_u *sep, int echo, int copyID));
 static int free_unref_items __ARGS((int copyID));
-static int rettv_dict_alloc __ARGS((typval_T *rettv));
 static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
 static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
 static dict_T *dict_copy __ARGS((dict_T *orig, int deep, int copyID));
@@ -453,9 +454,6 @@ static int get_dict_tv __ARGS((char_u **
 static char_u *echo_string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID));
 static char_u *tv2string __ARGS((typval_T *tv, char_u **tofree, char_u *numbuf, int copyID));
 static char_u *string_quote __ARGS((char_u *str, int function));
-#ifdef FEAT_FLOAT
-static int string2float __ARGS((char_u *text, float_T *value));
-#endif
 static int get_env_tv __ARGS((char_u **arg, typval_T *rettv, int evaluate));
 static int find_internal_func __ARGS((char_u *name));
 static char_u *deref_func_name __ARGS((char_u *name, int *lenp, int no_autoload));
@@ -617,6 +615,8 @@ static void f_isdirectory __ARGS((typval
 static void f_islocked __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_items __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_join __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_jsondecode __ARGS((typval_T *argvars, typval_T *rettv));
+static void f_jsonencode __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_keys __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_last_buffer_nr __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_len __ARGS((typval_T *argvars, typval_T *rettv));
@@ -816,7 +816,6 @@ static linenr_T get_tv_lnum __ARGS((typv
 static linenr_T get_tv_lnum_buf __ARGS((typval_T *argvars, buf_T *buf));
 static char_u *get_tv_string __ARGS((typval_T *varp));
 static char_u *get_tv_string_buf __ARGS((typval_T *varp, char_u *buf));
-static char_u *get_tv_string_buf_chk __ARGS((typval_T *varp, char_u *buf));
 static dictitem_T *find_var __ARGS((char_u *name, hashtab_T **htp, int no_autoload));
 static dictitem_T *find_var_in_ht __ARGS((hashtab_T *ht, int htname, char_u *varname, int no_autoload));
 static hashtab_T *find_var_ht __ARGS((char_u *name, char_u **varname));
@@ -915,6 +914,12 @@ eval_init()
     set_vim_var_nr(VV_HLSEARCH, 1L);
     set_vim_var_dict(VV_COMPLETED_ITEM, dict_alloc());
     set_vim_var_list(VV_ERRORS, list_alloc());
+
+    set_vim_var_nr(VV_FALSE, VVAL_FALSE);
+    set_vim_var_nr(VV_TRUE, VVAL_TRUE);
+    set_vim_var_nr(VV_NONE, VVAL_NONE);
+    set_vim_var_nr(VV_NULL, VVAL_NULL);
+
     set_reg_var(0);  /* default for v:register is not 0 but '"' */
 
 #ifdef EBCDIC
@@ -3080,13 +3085,15 @@ tv_op(tv1, tv2, op)
     char_u	numbuf[NUMBUFLEN];
     char_u	*s;
 
-    /* Can't do anything with a Funcref or a Dict on the right. */
-    if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT)
+    /* Can't do anything with a Funcref, Dict, v:true on the right. */
+    if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT
+						&& tv2->v_type != VAR_SPECIAL)
     {
 	switch (tv1->v_type)
 	{
 	    case VAR_DICT:
 	    case VAR_FUNC:
+	    case VAR_SPECIAL:
 		break;
 
 	    case VAR_LIST:
@@ -5390,6 +5397,10 @@ eval_index(arg, rettv, evaluate, verbose
 	return FAIL;
     }
 #endif
+    else if (rettv->v_type == VAR_SPECIAL)
+    {
+	return FAIL;
+    }
 
     init_tv(&var1);
     init_tv(&var2);
@@ -5999,7 +6010,7 @@ list_alloc()
  * Allocate an empty list for a return value.
  * Returns OK or FAIL.
  */
-    static int
+    int
 rettv_list_alloc(rettv)
     typval_T	*rettv;
 {
@@ -6246,6 +6257,9 @@ tv_equal(tv1, tv2, ic, recursive)
 	    s1 = get_tv_string_buf(tv1, buf1);
 	    s2 = get_tv_string_buf(tv2, buf2);
 	    return ((ic ? MB_STRICMP(s1, s2) : STRCMP(s1, s2)) == 0);
+
+	case VAR_SPECIAL:
+	    return tv1->vval.v_number == tv2->vval.v_number;
     }
 
     EMSG2(_(e_intern2), "tv_equal()");
@@ -6838,6 +6852,17 @@ list_join(gap, l, sep, echo_style, copyI
 }
 
 /*
+ * Return the next (unique) copy ID.
+ * Used for serializing nested structures.
+ */
+    int
+get_copyID()
+{
+    current_copyID += COPYID_INC;
+    return current_copyID;
+}
+
+/*
  * Garbage collection for lists and dictionaries.
  *
  * We use reference counts to be able to free most items right away when they
@@ -6883,8 +6908,7 @@ garbage_collect()
 
     /* We advance by two because we add one for items referenced through
      * previous_funccal. */
-    current_copyID += COPYID_INC;
-    copyID = current_copyID;
+    copyID = get_copyID();
 
     /*
      * 1. Go through all accessible variables and mark all lists and dicts
@@ -7236,7 +7260,7 @@ dict_alloc()
  * Allocate an empty dict for a return value.
  * Returns OK or FAIL.
  */
-    static int
+    int
 rettv_dict_alloc(rettv)
     typval_T	*rettv;
 {
@@ -7891,6 +7915,18 @@ echo_string(tv, tofree, numbuf, copyID)
 	    break;
 #endif
 
+	case VAR_SPECIAL:
+	    *tofree = NULL;
+	    switch (tv->vval.v_number)
+	    {
+		case VVAL_FALSE: r = (char_u *)"false"; break;
+		case VVAL_TRUE: r = (char_u *)"true"; break;
+		case VVAL_NONE: r = (char_u *)"none"; break;
+		case VVAL_NULL: r = (char_u *)"null"; break;
+		default: EMSG2(_(e_intern2), "echo_string(special)");
+	    }
+	    break;
+
 	default:
 	    EMSG2(_(e_intern2), "echo_string()");
 	    *tofree = NULL;
@@ -7932,6 +7968,7 @@ tv2string(tv, tofree, numbuf, copyID)
 	case VAR_NUMBER:
 	case VAR_LIST:
 	case VAR_DICT:
+	case VAR_SPECIAL:
 	    break;
 	default:
 	    EMSG2(_(e_intern2), "tv2string()");
@@ -7985,14 +8022,14 @@ string_quote(str, function)
     return s;
 }
 
-#ifdef FEAT_FLOAT
+#if defined(FEAT_FLOAT) || defined(PROTO)
 /*
  * Convert the string "text" to a floating point number.
  * This uses strtod().  setlocale(LC_NUMERIC, "C") has been used to make sure
  * this always uses a decimal point.
  * Returns the length of the text that was consumed.
  */
-    static int
+    int
 string2float(text, value)
     char_u	*text;
     float_T	*value;	    /* result stored here */
@@ -8237,6 +8274,8 @@ static struct fst
     {"islocked",	1, 1, f_islocked},
     {"items",		1, 1, f_items},
     {"join",		1, 2, f_join},
+    {"jsondecode",	1, 1, f_jsondecode},
+    {"jsonencode",	1, 1, f_jsonencode},
     {"keys",		1, 1, f_keys},
     {"last_buffer_nr",	0, 0, f_last_buffer_nr},/* obsolete */
     {"len",		1, 1, f_len},
@@ -14394,6 +14433,34 @@ f_join(argvars, rettv)
 }
 
 /*
+ * "jsondecode()" function
+ */
+    static void
+f_jsondecode(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    js_read_T	reader;
+
+    reader.js_buf = get_tv_string(&argvars[0]);
+    reader.js_eof = TRUE;
+    reader.js_used = 0;
+    json_decode(&reader, rettv);
+}
+
+/*
+ * "jsonencode()" function
+ */
+    static void
+f_jsonencode(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = json_encode(&argvars[0]);
+}
+
+/*
  * "keys()" function
  */
     static void
@@ -21558,6 +21625,7 @@ clear_tv(varp)
 		varp->vval.v_dict = NULL;
 		break;
 	    case VAR_NUMBER:
+	    case VAR_SPECIAL:
 		varp->vval.v_number = 0;
 		break;
 #ifdef FEAT_FLOAT
@@ -21759,7 +21827,7 @@ get_tv_string_chk(varp)
     return get_tv_string_buf_chk(varp, mybuf);
 }
 
-    static char_u *
+    char_u *
 get_tv_string_buf_chk(varp, buf)
     typval_T	*varp;
     char_u	*buf;
@@ -22120,8 +22188,7 @@ list_one_var(v, prefix, first)
     char_u	*s;
     char_u	numbuf[NUMBUFLEN];
 
-    current_copyID += COPYID_INC;
-    s = echo_string(&v->di_tv, &tofree, numbuf, current_copyID);
+    s = echo_string(&v->di_tv, &tofree, numbuf, get_copyID());
     list_one_var_a(prefix, v->di_key, v->di_tv.v_type,
 					 s == NULL ? (char_u *)"" : s, first);
     vim_free(tofree);
@@ -22435,6 +22502,7 @@ copy_tv(from, to)
     switch (from->v_type)
     {
 	case VAR_NUMBER:
+	case VAR_SPECIAL:
 	    to->vval.v_number = from->vval.v_number;
 	    break;
 #ifdef FEAT_FLOAT
@@ -22609,8 +22677,7 @@ ex_echo(eap)
 	    }
 	    else if (eap->cmdidx == CMD_echo)
 		msg_puts_attr((char_u *)" ", echo_attr);
-	    current_copyID += COPYID_INC;
-	    p = echo_string(&rettv, &tofree, numbuf, current_copyID);
+	    p = echo_string(&rettv, &tofree, numbuf, get_copyID());
 	    if (p != NULL)
 		for ( ; *p != NUL && !got_int; ++p)
 		{
--- a/src/globals.h
+++ b/src/globals.h
@@ -1523,6 +1523,7 @@ EXTERN char_u e_readonly[]	INIT(= N_("E4
 #ifdef FEAT_EVAL
 EXTERN char_u e_readonlyvar[]	INIT(= N_("E46: Cannot change read-only variable \"%s\""));
 EXTERN char_u e_readonlysbx[]	INIT(= N_("E794: Cannot set variable in the sandbox: \"%s\""));
+EXTERN char_u e_emptykey[]	INIT(= N_("E713: Cannot use empty key for Dictionary"));
 #endif
 #ifdef FEAT_QUICKFIX
 EXTERN char_u e_readerrf[]	INIT(= N_("E47: Error while reading errorfile"));
--- a/src/if_lua.c
+++ b/src/if_lua.c
@@ -499,6 +499,12 @@ luaV_pushtypval(lua_State *L, typval_T *
 	case VAR_DICT:
 	    luaV_pushdict(L, tv->vval.v_dict);
 	    break;
+	case VAR_SPECIAL:
+	    if (tv->vval.v_number <= VVAL_TRUE)
+		lua_pushinteger(L, (int) tv->vval.v_number);
+	    else
+		lua_pushnil(L);
+	    break;
 	default:
 	    lua_pushnil(L);
     }
@@ -510,7 +516,7 @@ luaV_totypval (lua_State *L, int pos, ty
 {
     switch(lua_type(L, pos)) {
 	case LUA_TBOOLEAN:
-	    tv->v_type = VAR_NUMBER;
+	    tv->v_type = VAR_SPECIAL;
 	    tv->vval.v_number = (varnumber_T) lua_toboolean(L, pos);
 	    break;
 	case LUA_TSTRING:
--- a/src/if_mzsch.c
+++ b/src/if_mzsch.c
@@ -3084,6 +3084,14 @@ vim_to_mzscheme_impl(typval_T *vim_value
 
 	MZ_GC_UNREG();
     }
+    else if (vim_value->v_type == VAR_SPECIAL)
+    {
+	if (vim_value->vval.v_number <= VVAL_TRUE)
+	    result = scheme_make_integer((long)vim_value->vval.v_number);
+	else
+	    result = scheme_null;
+	MZ_GC_CHECK();
+    }
     else
     {
 	result = scheme_void;
@@ -3148,8 +3156,8 @@ mzscheme_to_vim_impl(Scheme_Object *obj,
 	copy_tv(found, tv);
     else if (SCHEME_VOIDP(obj))
     {
-	tv->v_type = VAR_NUMBER;
-	tv->vval.v_number = 0;
+	tv->v_type = VAR_SPECIAL;
+	tv->vval.v_number = VVAL_NULL;
     }
     else if (SCHEME_INTP(obj))
     {
@@ -3158,7 +3166,7 @@ mzscheme_to_vim_impl(Scheme_Object *obj,
     }
     else if (SCHEME_BOOLP(obj))
     {
-	tv->v_type = VAR_NUMBER;
+	tv->v_type = VAR_SPECIAL;
 	tv->vval.v_number = SCHEME_TRUEP(obj);
     }
 # ifdef FEAT_FLOAT
--- a/src/if_py_both.h
+++ b/src/if_py_both.h
@@ -810,6 +810,25 @@ VimToPython(typval_T *our_tv, int depth,
 	    }
 	}
     }
+    else if (our_tv->v_type == VAR_SPECIAL)
+    {
+	if (our_tv->vval.v_number == VVAL_FALSE)
+	{
+	    ret = Py_False;
+	    Py_INCREF(ret);
+	}
+	else if (our_tv->vval.v_number == VVAL_TRUE)
+	{
+	    ret = Py_True;
+	    Py_INCREF(ret);
+	}
+	else
+	{
+	    Py_INCREF(Py_None);
+	    ret = Py_None;
+	}
+	return ret;
+    }
     else
     {
 	Py_INCREF(Py_None);
--- a/src/if_ruby.c
+++ b/src/if_ruby.c
@@ -1032,6 +1032,11 @@ static VALUE vim_to_ruby(typval_T *tv)
 		}
 	    }
 	}
+    }
+    else if (tv->v_type == VAR_SPECIAL)
+    {
+	if (tv->vval.v_number <= VVAL_TRUE)
+	    result = INT2NUM(tv->vval.v_number);
     } /* else return Qnil; */
 
     return result;
new file mode 100644
--- /dev/null
+++ b/src/json.c
@@ -0,0 +1,509 @@
+/* vi:set ts=8 sts=4 sw=4:
+ *
+ * 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.
+ */
+
+/*
+ * json.c: Encoding and decoding JSON.
+ *
+ * Follows this standard: http://www.ietf.org/rfc/rfc4627.txt
+ */
+
+#include "vim.h"
+
+#if defined(FEAT_EVAL) || defined(PROTO)
+static void json_decode_item(js_read_T *reader, typval_T *res);
+
+/*
+ * Encode "val" into a JSON format string.
+ */
+    char_u *
+json_encode(typval_T *val)
+{
+    garray_T ga;
+
+    /* Store bytes in the growarray. */
+    ga_init2(&ga, 1, 4000);
+    json_encode_item(&ga, val, get_copyID());
+    return ga.ga_data;
+}
+
+    static void
+write_string(garray_T *gap, char_u *str)
+{
+    char_u	*res = str;
+    char_u	numbuf[NUMBUFLEN];
+
+    if (res == NULL)
+	ga_concat(gap, (char_u *)"null");
+    else
+    {
+	ga_append(gap, '"');
+	while (*res != NUL)
+	{
+	    int c = PTR2CHAR(res);
+
+	    switch (c)
+	    {
+		case 0x08:
+		    ga_append(gap, '\\'); ga_append(gap, 'b'); break;
+		case 0x09:
+		    ga_append(gap, '\\'); ga_append(gap, 't'); break;
+		case 0x0a:
+		    ga_append(gap, '\\'); ga_append(gap, 'n'); break;
+		case 0x0c:
+		    ga_append(gap, '\\'); ga_append(gap, 'f'); break;
+		case 0x0d:
+		    ga_append(gap, '\\'); ga_append(gap, 'r'); break;
+		case 0x22: /* " */
+		case 0x5c: /* \ */
+		    ga_append(gap, '\\');
+		    ga_append(gap, c);
+		    break;
+		default:
+		    if (c >= 0x20)
+		    {
+			numbuf[mb_char2bytes(c, numbuf)] = NUL;
+			ga_concat(gap, numbuf);
+		    }
+		    else
+		    {
+			vim_snprintf((char *)numbuf, NUMBUFLEN,
+							 "\\u%04lx", (long)c);
+			ga_concat(gap, numbuf);
+		    }
+	    }
+	    mb_cptr_adv(res);
+	}
+	ga_append(gap, '"');
+    }
+}
+
+    void
+json_encode_item(garray_T *gap, typval_T *val, int copyID)
+{
+    char_u	numbuf[NUMBUFLEN];
+    char_u	*res;
+    list_T	*l;
+    dict_T	*d;
+
+    switch (val->v_type)
+    {
+	case VAR_SPECIAL:
+	    switch(val->vval.v_number)
+	    {
+		case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
+		case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
+		case VVAL_NONE: break;
+		case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
+	    }
+	    break;
+
+	case VAR_NUMBER:
+	    vim_snprintf((char *)numbuf, NUMBUFLEN, "%ld",
+						    (long)val->vval.v_number);
+	    ga_concat(gap, numbuf);
+	    break;
+
+	case VAR_STRING:
+	    res = val->vval.v_string;
+	    write_string(gap, res);
+	    break;
+
+	case VAR_FUNC:
+	    /* no JSON equivalent, skip */
+	    break;
+
+	case VAR_LIST:
+	    l = val->vval.v_list;
+	    if (l == NULL)
+		ga_concat(gap, (char_u *)"null");
+	    else
+	    {
+		if (l->lv_copyID == copyID)
+		    ga_concat(gap, (char_u *)"[]");
+		else
+		{
+		    listitem_T	*li;
+
+		    l->lv_copyID = copyID;
+		    ga_append(gap, '[');
+		    for (li = l->lv_first; li != NULL && !got_int; )
+		    {
+			json_encode_item(gap, &li->li_tv, copyID);
+			li = li->li_next;
+			if (li != NULL)
+			    ga_append(gap, ',');
+		    }
+		    ga_append(gap, ']');
+		}
+	    }
+	    break;
+
+	case VAR_DICT:
+	    d = val->vval.v_dict;
+	    if (d == NULL)
+		ga_concat(gap, (char_u *)"null");
+	    else
+	    {
+		if (d->dv_copyID == copyID)
+		    ga_concat(gap, (char_u *)"{}");
+		else
+		{
+		    int		first = TRUE;
+		    int		todo = (int)d->dv_hashtab.ht_used;
+		    hashitem_T	*hi;
+
+		    d->dv_copyID = copyID;
+		    ga_append(gap, '{');
+
+		    for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
+									 ++hi)
+			if (!HASHITEM_EMPTY(hi))
+			{
+			    --todo;
+			    if (first)
+				first = FALSE;
+			    else
+				ga_append(gap, ',');
+			    write_string(gap, hi->hi_key);
+			    ga_append(gap, ':');
+			    json_encode_item(gap, &dict_lookup(hi)->di_tv,
+								      copyID);
+			}
+		    ga_append(gap, '}');
+		}
+	    }
+	    break;
+
+#ifdef FEAT_FLOAT
+	case VAR_FLOAT:
+	    vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float);
+	    ga_concat(gap, numbuf);
+	    break;
+#endif
+	default: EMSG2(_(e_intern2), "json_encode_item()"); break;
+    }
+}
+
+/*
+ * Skip white space in "reader".
+ */
+    static void
+json_skip_white(js_read_T *reader)
+{
+    int c;
+
+    while ((c = reader->js_buf[reader->js_used]) == ' '
+					   || c == TAB || c == NL || c == CAR)
+	++reader->js_used;
+}
+
+/*
+ * Make sure there are at least enough characters buffered to read a number.
+ */
+    static void
+json_fill_buffer(js_read_T *reader UNUSED)
+{
+    /* TODO */
+}
+
+    static void
+json_decode_array(js_read_T *reader, typval_T *res)
+{
+    char_u	*p;
+    typval_T	item;
+    listitem_T	*li;
+
+    if (rettv_list_alloc(res) == FAIL)
+	goto fail;
+    ++reader->js_used; /* consume the '[' */
+
+    while (TRUE)
+    {
+	json_skip_white(reader);
+	p = reader->js_buf + reader->js_used;
+	if (*p == NUL)
+	    goto fail;
+	if (*p == ']')
+	{
+	    ++reader->js_used; /* consume the ']' */
+	    return;
+	}
+
+	if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
+	    json_fill_buffer(reader);
+
+	json_decode_item(reader, &item);
+	li = listitem_alloc();
+	if (li == NULL)
+	    return;
+	li->li_tv = item;
+	list_append(res->vval.v_list, li);
+
+	json_skip_white(reader);
+	p = reader->js_buf + reader->js_used;
+	if (*p == ',')
+	    ++reader->js_used;
+	else if (*p != ']')
+	    goto fail;
+    }
+fail:
+    res->v_type = VAR_SPECIAL;
+    res->vval.v_number = VVAL_NONE;
+}
+
+    static void
+json_decode_object(js_read_T *reader, typval_T *res)
+{
+    char_u	*p;
+    typval_T	tvkey;
+    typval_T	item;
+    dictitem_T	*di;
+    char_u	buf[NUMBUFLEN];
+    char_u	*key;
+
+    if (rettv_dict_alloc(res) == FAIL)
+	goto fail;
+    ++reader->js_used; /* consume the '{' */
+
+    while (TRUE)
+    {
+	json_skip_white(reader);
+	p = reader->js_buf + reader->js_used;
+	if (*p == NUL)
+	    goto fail;
+	if (*p == '}')
+	{
+	    ++reader->js_used; /* consume the '}' */
+	    return;
+	}
+
+	if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
+	    json_fill_buffer(reader);
+	json_decode_item(reader, &tvkey);
+	key = get_tv_string_buf_chk(&tvkey, buf);
+	if (key == NULL || *key == NUL)
+	{
+	    /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
+	    if (key != NULL)
+		EMSG(_(e_emptykey));
+	    clear_tv(&tvkey);
+	    goto fail;
+	}
+
+	json_skip_white(reader);
+	p = reader->js_buf + reader->js_used;
+	if (*p != ':')
+	{
+	    clear_tv(&tvkey);
+	    goto fail;
+	}
+	++reader->js_used;
+	json_skip_white(reader);
+
+	if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
+	    json_fill_buffer(reader);
+	json_decode_item(reader, &item);
+
+	di = dictitem_alloc(key);
+	clear_tv(&tvkey);
+	if (di == NULL)
+	{
+	    clear_tv(&item);
+	    goto fail;
+	}
+	di->di_tv = item;
+	dict_add(res->vval.v_dict, di);
+
+	json_skip_white(reader);
+	p = reader->js_buf + reader->js_used;
+	if (*p == ',')
+	    ++reader->js_used;
+	else if (*p != '}')
+	    goto fail;
+    }
+fail:
+    res->v_type = VAR_SPECIAL;
+    res->vval.v_number = VVAL_NONE;
+}
+
+    static void
+json_decode_string(js_read_T *reader, typval_T *res)
+{
+    garray_T    ga;
+    int		len;
+    char_u	*p = reader->js_buf + reader->js_used + 1;
+    int		c;
+    long	nr;
+    char_u	buf[NUMBUFLEN];
+
+    ga_init2(&ga, 1, 200);
+
+    /* TODO: fill buffer when needed. */
+    while (*p != NUL && *p != '"')
+    {
+	if (*p == '\\')
+	{
+	    c = -1;
+	    switch (p[1])
+	    {
+		case 'b': c = BS; break;
+		case 't': c = TAB; break;
+		case 'n': c = NL; break;
+		case 'f': c = FF; break;
+		case 'r': c = CAR; break;
+		case 'u':
+		    vim_str2nr(p + 2, NULL, &len,
+				     STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
+		    p += len + 2;
+#ifdef FEAT_MBYTE
+		    buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
+		    ga_concat(&ga, buf);
+#else
+		    ga_append(&ga, nr);
+#endif
+		    break;
+		default: c = p[1]; break;
+	    }
+	    if (c > 0)
+	    {
+		p += 2;
+		ga_append(&ga, c);
+	    }
+	}
+	else
+	{
+	    len = MB_PTR2LEN(p);
+	    if (ga_grow(&ga, len) == OK)
+	    {
+		mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
+		ga.ga_len += len;
+	    }
+	    p += len;
+	}
+	if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
+	{
+	    reader->js_used = (int)(p - reader->js_buf);
+	    json_fill_buffer(reader);
+	    p = reader->js_buf + reader->js_used;
+	}
+    }
+    reader->js_used = (int)(p - reader->js_buf);
+    if (*p == '"')
+    {
+	++reader->js_used;
+	res->v_type = VAR_STRING;
+	res->vval.v_string = vim_strsave(ga.ga_data);
+    }
+    else
+    {
+	res->v_type = VAR_SPECIAL;
+	res->vval.v_number = VVAL_NONE;
+    }
+    ga_clear(&ga);
+}
+
+/*
+ * Decode one item and put it in "result".
+ * Must already have skipped white space.
+ */
+    static void
+json_decode_item(js_read_T *reader, typval_T *res)
+{
+    char_u	*p = reader->js_buf + reader->js_used;
+
+    switch (*p)
+    {
+	case '[': /* array */
+	    json_decode_array(reader, res);
+	    return;
+
+	case '{': /* object */
+	    json_decode_object(reader, res);
+	    return;
+
+	case '"': /* string */
+	    json_decode_string(reader, res);
+	    return;
+
+	case ',': /* comma: empty item */
+	case NUL: /* empty */
+	    res->v_type = VAR_SPECIAL;
+	    res->vval.v_number = VVAL_NONE;
+	    return;
+
+	default:
+	    if (VIM_ISDIGIT(*p) || *p == '-')
+	    {
+		int	len;
+		char_u  *sp = p;
+#ifdef FEAT_FLOAT
+		if (*sp == '-')
+		    ++sp;
+		sp = skipdigits(sp);
+		if (*sp == '.' || *sp == 'e' || *sp == 'E')
+		{
+		    res->v_type = VAR_FLOAT;
+		    len = string2float(p, &res->vval.v_float);
+		}
+		else
+#endif
+		{
+		    long nr;
+
+		    res->v_type = VAR_NUMBER;
+		    vim_str2nr(reader->js_buf + reader->js_used,
+			    NULL, &len, 0, /* what */
+			    &nr, NULL, 0);
+		    res->vval.v_number = nr;
+		}
+		reader->js_used += len;
+		return;
+	    }
+	    if (STRNICMP((char *)p, "false", 5) == 0)
+	    {
+		reader->js_used += 5;
+		res->v_type = VAR_SPECIAL;
+		res->vval.v_number = VVAL_FALSE;
+		return;
+	    }
+	    if (STRNICMP((char *)p, "true", 4) == 0)
+	    {
+		reader->js_used += 4;
+		res->v_type = VAR_SPECIAL;
+		res->vval.v_number = VVAL_TRUE;
+		return;
+	    }
+	    if (STRNICMP((char *)p, "null", 4) == 0)
+	    {
+		reader->js_used += 4;
+		res->v_type = VAR_SPECIAL;
+		res->vval.v_number = VVAL_NULL;
+		return;
+	    }
+	    break;
+    }
+
+    EMSG(_(e_invarg));
+    res->v_type = VAR_SPECIAL;
+    res->vval.v_number = VVAL_NONE;
+}
+
+/*
+ * Decode the JSON from "reader" and store the result in "res".
+ */
+    void
+json_decode(js_read_T *reader, typval_T *res)
+{
+    json_skip_white(reader);
+    json_decode_item(reader, res);
+    json_skip_white(reader);
+    if (reader->js_buf[reader->js_used] != NUL)
+	EMSG(_(e_invarg));
+}
+#endif
--- a/src/proto.h
+++ b/src/proto.h
@@ -95,6 +95,7 @@ extern int _stricoll __ARGS((char *a, ch
 # endif
 # include "hardcopy.pro"
 # include "hashtab.pro"
+# include "json.pro"
 # include "main.pro"
 # include "mark.pro"
 # include "memfile.pro"
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -46,6 +46,7 @@ int do_unlet(char_u *name, int forceit);
 void del_menutrans_vars(void);
 char_u *get_user_var_name(expand_T *xp, int idx);
 list_T *list_alloc(void);
+int rettv_list_alloc(typval_T *rettv);
 void list_unref(list_T *l);
 void list_free(list_T *l, int recurse);
 listitem_T *listitem_alloc(void);
@@ -61,11 +62,13 @@ int list_append_string(list_T *l, char_u
 int list_insert_tv(list_T *l, typval_T *tv, listitem_T *item);
 void list_insert(list_T *l, listitem_T *ni, listitem_T *item);
 void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2);
+int get_copyID(void);
 int garbage_collect(void);
 int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack);
 int set_ref_in_list(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);
 dict_T *dict_alloc(void);
+int rettv_dict_alloc(typval_T *rettv);
 void dict_unref(dict_T *d);
 void dict_free(dict_T *d, int recurse);
 dictitem_T *dictitem_alloc(char_u *key);
@@ -76,6 +79,7 @@ int dict_add_list(dict_T *d, char *key, 
 dictitem_T *dict_find(dict_T *d, char_u *key, int len);
 char_u *get_dict_string(dict_T *d, char_u *key, int save);
 long get_dict_number(dict_T *d, char_u *key);
+int string2float(char_u *text, float_T *value);
 char_u *get_function_name(expand_T *xp, int idx);
 char_u *get_expr_name(expand_T *xp, int idx);
 int func_call(char_u *name, typval_T *args, dict_T *selfdict, typval_T *rettv);
@@ -100,6 +104,7 @@ void free_tv(typval_T *varp);
 void clear_tv(typval_T *varp);
 long get_tv_number_chk(typval_T *varp, int *denote);
 char_u *get_tv_string_chk(typval_T *varp);
+char_u *get_tv_string_buf_chk(typval_T *varp, char_u *buf);
 char_u *get_var_value(char_u *name);
 void new_script_vars(scid_T id);
 void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope);
new file mode 100644
--- /dev/null
+++ b/src/proto/json.pro
@@ -0,0 +1,5 @@
+/* json.c */
+char_u *json_encode(typval_T *val);
+void json_encode_item(garray_T *gap, typval_T *val, int copyID);
+void json_decode(js_read_T *reader, typval_T *res);
+/* vim: set ft=c : */
--- a/src/structs.h
+++ b/src/structs.h
@@ -1138,6 +1138,7 @@ typedef struct
 #define VAR_LIST    4	/* "v_list" is used */
 #define VAR_DICT    5	/* "v_dict" is used */
 #define VAR_FLOAT   6	/* "v_float" is used */
+#define VAR_SPECIAL 7	/* "v_number" is used */
 
 /* Values for "dv_scope". */
 #define VAR_SCOPE     1	/* a:, v:, s:, etc. scope dictionaries */
@@ -2682,3 +2683,15 @@ typedef struct {
   UINT32_T state[8];
   char_u   buffer[64];
 } context_sha256_T;
+
+/*
+ * Structure used for reading in json_decode().
+ */
+typedef struct
+{
+    char_u	*js_buf;	/* text to be decoded */
+    char_u	*js_end;	/* NUL in js_buf when js_eof is FALSE */
+    int		js_used;	/* bytes used from js_buf */
+    int		js_eof;		/* when TRUE js_buf is all there is */
+    FILE	*js_fd;		/* file descriptor to read more from */
+} js_read_T;
--- a/src/testdir/test_alot.vim
+++ b/src/testdir/test_alot.vim
@@ -5,6 +5,7 @@ source test_backspace_opt.vim
 source test_cursor_func.vim
 source test_delete.vim
 source test_expand.vim
+source test_json.vim
 source test_lispwords.vim
 source test_menu.vim
 source test_searchpos.vim
new file mode 100644
--- /dev/null
+++ b/src/testdir/test_json.vim
@@ -0,0 +1,91 @@
+" Test for JSON functions.
+
+let s:json1 = '"str\"in\\g"'
+let s:var1 = "str\"in\\g"
+let s:json2 = '"\u0001\u0002\u0003\u0004\u0005\u0006\u0007"'
+let s:var2 = "\x01\x02\x03\x04\x05\x06\x07"
+let s:json3 = '"\b\t\n\u000b\f\r\u000e\u000f"'
+let s:var3 = "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+let s:json4 = '"\u0010\u0011\u0012\u0013\u0014\u0015\u0016\u0017"'
+let s:var4 = "\x10\x11\x12\x13\x14\x15\x16\x17"
+let s:json5 = '"\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f"'
+let s:var5 = "\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
+
+let s:jsonmb = '"s¢cĴgё"'
+let s:varmb = "s¢cĴgё"
+let s:jsonnr = '1234'
+let s:varnr = 1234
+let s:jsonfl = '12.34'
+let s:varfl = 12.34
+
+let s:jsonl1 = '[1,"a",3]'
+let s:varl1 = [1, "a", 3]
+let s:jsonl2 = '[1,["a",[],"c"],3]'
+let s:jsonl2s = "  [\r1  ,  [  \"a\"  ,  [  ]  ,  \"c\"  ]  ,  3\<Tab>]\r\n"
+let s:varl2 = [1, 2, 3]
+let l2 = ['a', s:varl2, 'c']
+let s:varl2[1] = l2
+let s:varl2x = [1, ["a", [], "c"], 3]
+
+let s:jsond1 = '{"a":1,"b":"bee","c":[1,2]}'
+let s:vard1 = {"a": 1, "b": "bee","c": [1,2]}
+let s:jsond2 = '{"1":1,"2":{"a":"aa","b":{},"c":"cc"},"3":3}'
+let s:jsond2s = "  { \"1\" : 1 , \"2\" :\n{ \"a\"\r: \"aa\" , \"b\" : {\<Tab>} , \"c\" : \"cc\" } , \"3\" : 3 }\r\n"
+let s:vard2 = {"1": 1, "2": 2, "3": 3}
+let d2 = {"a": "aa", "b": s:vard2, "c": "cc"}
+let s:vard2["2"] = d2
+let s:vard2x = {"1": 1, "2": {"a": "aa", "b": {}, "c": "cc"}, "3": 3}
+
+let s:jsonvals = '[true,false,,null]'
+let s:varvals = [v:true, v:false, v:none, v:null]
+
+func Test_encode()
+  call assert_equal(s:json1, jsonencode(s:var1))
+  call assert_equal(s:json2, jsonencode(s:var2))
+  call assert_equal(s:json3, jsonencode(s:var3))
+  call assert_equal(s:json4, jsonencode(s:var4))
+  call assert_equal(s:json5, jsonencode(s:var5))
+
+  if has('multi_byte')
+    call assert_equal(s:jsonmb, jsonencode(s:varmb))
+  endif
+
+  call assert_equal(s:jsonnr, jsonencode(s:varnr))
+  if has('float')
+    call assert_equal(s:jsonfl, jsonencode(s:varfl))
+  endif
+
+  call assert_equal(s:jsonl1, jsonencode(s:varl1))
+  call assert_equal(s:jsonl2, jsonencode(s:varl2))
+
+  call assert_equal(s:jsond1, jsonencode(s:vard1))
+  call assert_equal(s:jsond2, jsonencode(s:vard2))
+
+  call assert_equal(s:jsonvals, jsonencode(s:varvals))
+endfunc
+
+func Test_decode()
+  call assert_equal(s:var1, jsondecode(s:json1))
+  call assert_equal(s:var2, jsondecode(s:json2))
+  call assert_equal(s:var3, jsondecode(s:json3))
+  call assert_equal(s:var4, jsondecode(s:json4))
+  call assert_equal(s:var5, jsondecode(s:json5))
+
+  if has('multi_byte')
+    call assert_equal(s:varmb, jsondecode(s:jsonmb))
+  endif
+
+  call assert_equal(s:varnr, jsondecode(s:jsonnr))
+  if has('float')
+    call assert_equal(s:varfl, jsondecode(s:jsonfl))
+  endif
+
+  call assert_equal(s:varl1, jsondecode(s:jsonl1))
+  call assert_equal(s:varl2x, jsondecode(s:jsonl2))
+  call assert_equal(s:varl2x, jsondecode(s:jsonl2s))
+
+  call assert_equal(s:vard1, jsondecode(s:jsond1))
+  call assert_equal(s:vard2x, jsondecode(s:jsond2))
+
+  call assert_equal(s:varvals, jsondecode(s:jsonvals))
+endfunc
--- a/src/version.c
+++ b/src/version.c
@@ -742,6 +742,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1154,
+/**/
     1153,
 /**/
     1152,
--- a/src/vim.h
+++ b/src/vim.h
@@ -1896,7 +1896,17 @@ typedef int proftime_T;	    /* dummy for
 #define VV_OPTION_OLD   60
 #define VV_OPTION_TYPE  61
 #define VV_ERRORS	62
-#define VV_LEN		63	/* number of v: vars */
+#define VV_FALSE	63
+#define VV_TRUE		64
+#define VV_NULL		65
+#define VV_NONE		66
+#define VV_LEN		67	/* number of v: vars */
+
+/* used for v_number in VAR_SPECIAL */
+#define VVAL_FALSE	0L
+#define VVAL_TRUE	1L
+#define VVAL_NONE	2L
+#define VVAL_NULL	3L
 
 #ifdef FEAT_CLIPBOARD