]> granicus.if.org Git - vim/commitdiff
patch 7.4.1154 v7.4.1154
authorBram Moolenaar <Bram@vim.org>
Sat, 23 Jan 2016 18:46:28 +0000 (19:46 +0100)
committerBram Moolenaar <Bram@vim.org>
Sat, 23 Jan 2016 18:46:28 +0000 (19:46 +0100)
Problem:    No support for JSON.
Solution:   Add jsonencode() and jsondecode().  Also add v:false, v:true,
            v:null and v:none.

28 files changed:
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 [new file with mode: 0644]
src/proto.h
src/proto/eval.pro
src/proto/json.pro [new file with mode: 0644]
src/structs.h
src/testdir/test_alot.vim
src/testdir/test_json.vim [new file with mode: 0644]
src/version.c
src/vim.h

index 34ff0bd26c748be3d23a1ce04bd771c635296bbd..0032b76241cfc05a4fb0a4d8b9e20914650a0ada 100644 (file)
--- 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 \
index 2073f289e4423d20b7641b9077da7880b897a6df..c227d0c4be525b866cf3ac26c90630830da298b6 100644 (file)
@@ -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 most recently caught and not
        :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 click obtained with |getchar()|.
                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 exception most recently caught and not
        :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 {directory} is a directory
 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.
index a23174fefb5ce28318e703fbd451d0091c0770c8..0de784766358841b8250ac7ed8894ff0f40beeff 100644 (file)
@@ -72,6 +72,7 @@ EXE_dependencies = \
        getchar.obj \
        hardcopy.obj \
        hashtab.obj \
+       json.obj \
        main.obj \
        mark.obj \
        memfile.obj \
index ddbdbe304a655a855bd2183d0e40355e4ffb9fa8..8dcb6e8b4cf6714f4eaf1a2a6f48f42eb3a0aff8 100644 (file)
@@ -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 \
index 203438a59721a2902a5ef8047cd4a74aae5009be..7c7acbd4b1fab5cd129478c968086ddf080c4b8c 100644 (file)
@@ -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 \
index 890f15d09c0469416929bf72f7a54f9444417d36..c20ba89ee41d1889400e774c8fd8765010e4a4c8 100644 (file)
@@ -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)
index a891a9743b0d6f4f0a762f58d1e5c12414feabc2..2dbe95768c8244228345f4be3200cc75e552c11b 100644 (file)
@@ -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
index dd1311ac87a273e1d71bbe32bde3b7dbddd15b0e..1574f056ef5fa27d3d6ffe2e215d219c7046c972 100644 (file)
@@ -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
index 15a74773c2c02ba10c8bd592ff2d4c87b28584a3..2f2f08b8e2715ef49acd0efb4a9006193caaa4b3 100644 (file)
@@ -43,6 +43,7 @@ SRC = blowfish.c                                              \
        getchar.c                                               \
        hardcopy.c                                              \
        hashtab.c                                               \
+       json.c                                                  \
        main.c                                                  \
        mark.c                                                  \
        mbyte.c                                                 \
index 217df5710a03b4d8dc3f50c655acb122dd693940..c009aa977700f585a790abcd348e1f58dd461e9d 100644 (file)
@@ -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 @@ $(OUTDIR)/if_sniff.obj:   $(OUTDIR) if_sniff.c  $(INCL)
 $(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 \
index 49dcb6bdcd611029cba9d0602e2e35a8edcaa079..6d2ca844f4efff3b00215841cb9bf61b0d0c7dd0 100644 (file)
@@ -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
index f19b68858502daf2e3bcb6ff277f376e1348f8ae..da535b90f5933cc0aeeea46880c753b9551c32ba 100644 (file)
@@ -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_LIB) \
 
 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 crypt.c, crypt_zip.c diff.c digraph.c edit.c
 
 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]config.h feature.h os_unix.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 
+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 \
index 4aa5a48b2b2a370e01e9fb67b6795c49002c2678..91f3b7719738c2110b2d15731de7eacd66b61649 100644 (file)
@@ -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/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 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/config.h feature.h os_unix.h auto/osdef.h
 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 feature.h os_unix.h auto/osdef.h ascii.
 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 auto/config.h feature.h os_unix.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/config.h feature.h os_unix.h auto/osdef.h
 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 \
index 565d71d78631075665d10ce7aaccee577d469fc2..a0cc6d0170e982ee5b495f4ce7be1f50c228bcbd 100644 (file)
@@ -99,7 +99,6 @@ static char *e_undefvar = N_("E121: Undefined variable: %s");
 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 **arg, typval_T *rettv, int evaluate));
 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((typval_T *tv, int copyID));
 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 **arg, typval_T *rettv, int evaluate));
 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_T *argvars, typval_T *rettv));
 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((typval_T *argvars));
 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()");
@@ -6837,6 +6851,17 @@ list_join(gap, l, sep, echo_style, copyID)
     return retval;
 }
 
+/*
+ * 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.
  *
@@ -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},
@@ -14393,6 +14432,34 @@ f_join(argvars, rettv)
        rettv->vval.v_string = NULL;
 }
 
+/*
+ * "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
  */
@@ -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)
                {
index 4dc0b81a1c57a53d3b3386fd83567aeca6a526b3..f252aeeea0b9c217b2c4b6ba754cee43fe247716 100644 (file)
@@ -1523,6 +1523,7 @@ EXTERN char_u e_readonly[]        INIT(= N_("E45: 'readonly' option is set (add ! to ov
 #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"));
index 46f881a9bddd8ed446b541c3641266a1e8c9bf0d..1c8512fec54052f667d1bad603769a0149c5d376 100644 (file)
@@ -499,6 +499,12 @@ luaV_pushtypval(lua_State *L, typval_T *tv)
        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, typval_T *tv)
 {
     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:
index ea0470545936cbfe22c299d528cc63e5b39fdf84..0884c9f5eea2f71ad6f61830fb8e07e7af3b12a0 100644 (file)
@@ -3084,6 +3084,14 @@ vim_to_mzscheme_impl(typval_T *vim_value, int depth, Scheme_Hash_Table *visited)
 
        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, typval_T *tv, int depth,
        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, typval_T *tv, int depth,
     }
     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
index e220e3fec7d312e949a6d24d9504c2cc10a4695c..e8a5f5de9cc8eced86a2c1e1e27f16d8d686bac3 100644 (file)
@@ -810,6 +810,25 @@ VimToPython(typval_T *our_tv, int depth, PyObject *lookup_dict)
            }
        }
     }
+    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);
index 96d9453dbc11f975b44afb8a1de7037eed3f2231..bb34de1316be371cbf9a2bfeb3370561f0abb26d 100644 (file)
@@ -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;
diff --git a/src/json.c b/src/json.c
new file mode 100644 (file)
index 0000000..7b67994
--- /dev/null
@@ -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
index 2b08eb38ffcb29f4724a95d5a1bdf5f9b4c7bf44..5f52a314b2eb2c13bb9d40aca951db13f4e47fbf 100644 (file)
@@ -95,6 +95,7 @@ extern int _stricoll __ARGS((char *a, char *b));
 # endif
 # include "hardcopy.pro"
 # include "hashtab.pro"
+# include "json.pro"
 # include "main.pro"
 # include "mark.pro"
 # include "memfile.pro"
index fae403a504703a0273068226eb93536cf293b0a2..f6ad4b49e8f9a0b67f0a94e5fb3aabe00ff65754 100644 (file)
@@ -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 *str, int len);
 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, list_T *list);
 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);
diff --git a/src/proto/json.pro b/src/proto/json.pro
new file mode 100644 (file)
index 0000000..6920f51
--- /dev/null
@@ -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 : */
index 143d46dfa9ff05f3150d18d054db8fe3f10ba29c..8c84b5a75c88ecf1216b71a8e9c7e3359f0b762d 100644 (file)
@@ -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;
index e89afb49adcbd68671f6a4398642a7990dff3a44..aceb073f699578bb252e699cdb5e32ffee4ad467 100644 (file)
@@ -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
diff --git a/src/testdir/test_json.vim b/src/testdir/test_json.vim
new file mode 100644 (file)
index 0000000..e658c97
--- /dev/null
@@ -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
index c964b3cad577a4033be4883ea61d0726f523fe54..85410e34be4803b3e456c7ea1e96e26fb4d80bd9 100644 (file)
@@ -741,6 +741,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1154,
 /**/
     1153,
 /**/
index 2ebaa00807562b31a603c18ef82b46f4c3bc1f8d..90a5353c3f269414b8f2291bd12a1ff6956b1374 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -1896,7 +1896,17 @@ typedef int proftime_T;      /* dummy for function prototypes */
 #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