changeset 2050:afcf9db31561 v7.2.336

updated for version 7.2.336 Problem: MzScheme interface can't evaluate an expression. Solution: Add mzeval(). (Sergey Khorev)
author Bram Moolenaar <bram@zimbu.org>
date Tue, 19 Jan 2010 15:55:06 +0100
parents 23d366df1938
children ef2890033e88
files runtime/doc/eval.txt runtime/doc/if_mzsch.txt runtime/doc/usr_41.txt src/eval.c src/if_mzsch.c src/proto/eval.pro src/proto/if_mzsch.pro src/testdir/Make_dos.mak src/testdir/Make_ming.mak src/testdir/Makefile src/testdir/main.aap src/testdir/test1.in src/testdir/test70.in src/testdir/test70.ok src/version.c
diffstat 15 files changed, 359 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -1826,6 +1826,7 @@ min( {list})			Number	minimum value of i
 mkdir( {name} [, {path} [, {prot}]])
 				Number	create directory {name}
 mode( [expr])			String	current editing mode
+mzeval( {expr})			any	evaluate |MzScheme| expression
 nextnonblank( {lnum})		Number	line nr of non-blank line >= {lnum}
 nr2char( {expr})		String	single char with ASCII value {expr}
 pathshorten( {expr})		String	shorten directory names in a path
@@ -4102,6 +4103,23 @@ mode([expr])	Return a string that indica
 		"c" or "n".
 		Also see |visualmode()|.
 
+mzeval({expr})							*mzeval()*
+		Evaluate MzScheme expression {expr} and return its result
+		convert to Vim data structures.
+		Numbers and strings are returned as they are.
+		Pairs (including lists and improper lists) and vectors are
+		returned as Vim |Lists|.
+		Hash tables are represented as Vim |Dictionary| type with keys
+		converted to strings.
+		All other types are converted to string with display function.
+		Examples: >
+		    :mz (define l (list 1 2 3))
+		    :mz (define h (make-hash)) (hash-set! h "list" l)
+		    :echo mzeval("l")
+		    :echo mzeval("h")
+<
+		{only available when compiled with the |+mzscheme| feature}
+
 nextnonblank({lnum})					*nextnonblank()*
 		Return the line number of the first line at or below {lnum}
 		that is not blank.  Example: >
--- a/runtime/doc/if_mzsch.txt
+++ b/runtime/doc/if_mzsch.txt
@@ -1,4 +1,4 @@
-*if_mzsch.txt*  For Vim version 7.2.  Last change: 2009 Jun 24
+*if_mzsch.txt*  For Vim version 7.2.  Last change: 2010 Jan 19
 
 
 		  VIM REFERENCE MANUAL    by Sergey Khorev
@@ -9,8 +9,9 @@ The MzScheme Interface to Vim				*mzsche
 1. Commands				|mzscheme-commands|
 2. Examples				|mzscheme-examples|
 3. Threads				|mzscheme-threads|
-4. The Vim access procedures		|mzscheme-vim|
-5. Dynamic loading			|mzscheme-dynamic|
+4. Vim access from MzScheme		|mzscheme-vim|
+5. mzeval() Vim function		|mzscheme-mzeval|
+6. Dynamic loading			|mzscheme-dynamic|
 
 {Vi does not have any of these commands}
 
@@ -142,7 +143,7 @@ Thread scheduling in the console version
 GUI version.
 
 ==============================================================================
-5. VIM Functions					*mzscheme-vim*
+4. Vim access from MzScheme				*mzscheme-vim*
 
 							*mzscheme-vimext*
 The 'vimext' module provides access to procedures defined in the MzScheme
@@ -231,7 +232,13 @@ Windows							    *mzscheme-window*
     (set-cursor (line . col) [window])  Set cursor position.
 
 ==============================================================================
-5. Dynamic loading				    *mzscheme-dynamic* *E815*
+5. mzeval() Vim function				    *mzscheme-mzeval*
+
+To facilitate bi-directional interface, you can use |mzeval| function to
+evaluate MzScheme expressions and pass their values to VimL.
+
+==============================================================================
+6. Dynamic loading				    *mzscheme-dynamic* *E815*
 
 On MS-Windows the MzScheme libraries can be loaded dynamically. The |:version|
 output then includes |+mzscheme/dyn|.
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -868,6 +868,8 @@ Various:
 	taglist()		get list of matching tags
 	tagfiles()		get a list of tags files
 
+	mzeval()		evaluate |MzScheme| expression
+
 ==============================================================================
 *41.7*	Defining a function
 
--- a/src/eval.c
+++ b/src/eval.c
@@ -433,7 +433,6 @@ static listitem_T *list_find __ARGS((lis
 static long list_find_nr __ARGS((list_T *l, long idx, int *errorp));
 static long list_idx_of_item __ARGS((list_T *l, listitem_T *item));
 static void list_append __ARGS((list_T *l, listitem_T *item));
-static int list_append_tv __ARGS((list_T *l, typval_T *tv));
 static int list_append_number __ARGS((list_T *l, varnumber_T n));
 static int list_insert_tv __ARGS((list_T *l, typval_T *tv, listitem_T *item));
 static int list_extend __ARGS((list_T	*l1, list_T *l2, listitem_T *bef));
@@ -448,12 +447,9 @@ static void set_ref_in_list __ARGS((list
 static void set_ref_in_item __ARGS((typval_T *tv, int copyID));
 static void dict_unref __ARGS((dict_T *d));
 static void dict_free __ARGS((dict_T *d, int recurse));
-static dictitem_T *dictitem_alloc __ARGS((char_u *key));
 static dictitem_T *dictitem_copy __ARGS((dictitem_T *org));
 static void dictitem_remove __ARGS((dict_T *dict, dictitem_T *item));
-static void dictitem_free __ARGS((dictitem_T *item));
 static dict_T *dict_copy __ARGS((dict_T *orig, int deep, int copyID));
-static int dict_add __ARGS((dict_T *d, dictitem_T *item));
 static long dict_len __ARGS((dict_T *d));
 static dictitem_T *dict_find __ARGS((dict_T *d, char_u *key, int len));
 static char_u *dict2string __ARGS((typval_T *tv, int copyID));
@@ -628,6 +624,9 @@ static void f_min __ARGS((typval_T *argv
 static void f_mkdir __ARGS((typval_T *argvars, typval_T *rettv));
 #endif
 static void f_mode __ARGS((typval_T *argvars, typval_T *rettv));
+#ifdef FEAT_MZSCHEME
+static void f_mzeval __ARGS((typval_T *argvars, typval_T *rettv));
+#endif
 static void f_nextnonblank __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_nr2char __ARGS((typval_T *argvars, typval_T *rettv));
 static void f_pathshorten __ARGS((typval_T *argvars, typval_T *rettv));
@@ -764,7 +763,6 @@ static void set_var __ARGS((char_u *name
 static int var_check_ro __ARGS((int flags, char_u *name));
 static int var_check_fixed __ARGS((int flags, char_u *name));
 static int tv_check_lock __ARGS((int lock, char_u *name));
-static void copy_tv __ARGS((typval_T *from, typval_T *to));
 static int item_copy __ARGS((typval_T *from, typval_T *to, int deep, int copyID));
 static char_u *find_option_end __ARGS((char_u **arg, int *opt_flags));
 static char_u *trans_function_name __ARGS((char_u **pp, int skip, int flags, funcdict_T *fd));
@@ -6155,7 +6153,7 @@ list_append(l, item)
  * Append typval_T "tv" to the end of list "l".
  * Return FAIL when out of memory.
  */
-    static int
+    int
 list_append_tv(l, tv)
     list_T	*l;
     typval_T	*tv;
@@ -6812,7 +6810,7 @@ dict_free(d, recurse)
  * Note that the value of the item "di_tv" still needs to be initialized!
  * Returns NULL when out of memory.
  */
-    static dictitem_T *
+    dictitem_T *
 dictitem_alloc(key)
     char_u	*key;
 {
@@ -6868,7 +6866,7 @@ dictitem_remove(dict, item)
 /*
  * Free a dict item.  Also clears the value.
  */
-    static void
+    void
 dictitem_free(item)
     dictitem_T *item;
 {
@@ -6948,7 +6946,7 @@ dict_copy(orig, deep, copyID)
  * Add item "item" to Dictionary "d".
  * Returns FAIL when out of memory and when key already existed.
  */
-    static int
+    int
 dict_add(d, item)
     dict_T	*d;
     dictitem_T	*item;
@@ -7699,6 +7697,9 @@ static struct fst
     {"mkdir",		1, 3, f_mkdir},
 #endif
     {"mode",		0, 1, f_mode},
+#ifdef FEAT_MZSCHEME
+    {"mzeval",		1, 1, f_mzeval},
+#endif
     {"nextnonblank",	1, 1, f_nextnonblank},
     {"nr2char",		1, 1, f_nr2char},
     {"pathshorten",	1, 1, f_pathshorten},
@@ -13591,6 +13592,23 @@ f_mode(argvars, rettv)
     rettv->v_type = VAR_STRING;
 }
 
+#ifdef FEAT_MZSCHEME
+/*
+ * "mzeval()" function
+ */
+    static void
+f_mzeval(argvars, rettv)
+    typval_T	*argvars;
+    typval_T	*rettv;
+{
+    char_u	*str;
+    char_u	buf[NUMBUFLEN];
+
+    str = get_tv_string_buf(&argvars[0], buf);
+    do_mzeval(str, rettv);
+}
+#endif
+
 /*
  * "nextnonblank()" function
  */
@@ -19274,7 +19292,7 @@ tv_check_lock(lock, name)
  * It is OK for "from" and "to" to point to the same item.  This is used to
  * make a copy later.
  */
-    static void
+    void
 copy_tv(from, to)
     typval_T *from;
     typval_T *to;
--- a/src/if_mzsch.c
+++ b/src/if_mzsch.c
@@ -170,6 +170,8 @@ static int mzscheme_init(void);
 #ifdef FEAT_EVAL
 static Scheme_Object *vim_to_mzscheme(typval_T *vim_value, int depth,
 	Scheme_Hash_Table *visited);
+static int mzscheme_to_vim(Scheme_Object *obj, typval_T *tv, int depth,
+	Scheme_Hash_Table *visited);
 #endif
 
 #ifdef MZ_PRECISE_GC
@@ -2733,6 +2735,225 @@ vim_to_mzscheme(typval_T *vim_value, int
     MZ_GC_UNREG();
     return result;
 }
+
+    static int
+mzscheme_to_vim(Scheme_Object *obj, typval_T *tv, int depth,
+	Scheme_Hash_Table *visited)
+{
+    int		status = OK;
+    typval_T	*found;
+    MZ_GC_CHECK();
+    if (depth > 100) /* limit the deepest recursion level */
+    {
+	tv->v_type = VAR_NUMBER;
+	tv->vval.v_number = 0;
+	return FAIL;
+    }
+
+    found = (typval_T *)scheme_hash_get(visited, obj);
+    if (found != NULL)
+	copy_tv(found, tv);
+    else if (SCHEME_VOIDP(obj))
+    {
+	tv->v_type = VAR_NUMBER;
+	tv->vval.v_number = 0;
+    }
+    else if (SCHEME_INTP(obj))
+    {
+	tv->v_type = VAR_NUMBER;
+	tv->vval.v_number = SCHEME_INT_VAL(obj);
+    }
+    else if (SCHEME_BOOLP(obj))
+    {
+	tv->v_type = VAR_NUMBER;
+	tv->vval.v_number = SCHEME_TRUEP(obj);
+    }
+# ifdef FEAT_FLOAT
+    else if (SCHEME_DBLP(obj))
+    {
+	tv->v_type = VAR_FLOAT;
+	tv->vval.v_float = SCHEME_DBL_VAL(obj);
+    }
+# endif
+    else if (SCHEME_STRINGP(obj))
+    {
+	tv->v_type = VAR_STRING;
+	tv->vval.v_string = vim_strsave((char_u *)SCHEME_STR_VAL(obj));
+    }
+    else if (SCHEME_VECTORP(obj) || SCHEME_NULLP(obj)
+	    || SCHEME_PAIRP(obj) || SCHEME_MUTABLE_PAIRP(obj))
+    {
+	list_T  *list = list_alloc();
+	if (list == NULL)
+	    status = FAIL;
+	else
+	{
+	    int		    i;
+	    Scheme_Object   *curr = NULL;
+	    Scheme_Object   *cval = NULL;
+	    /* temporary var to hold current element of vectors and pairs */
+	    typval_T	    *v;
+
+	    MZ_GC_DECL_REG(2);
+	    MZ_GC_VAR_IN_REG(0, curr);
+	    MZ_GC_VAR_IN_REG(1, cval);
+	    MZ_GC_REG();
+
+	    tv->v_type = VAR_LIST;
+	    tv->vval.v_list = list;
+	    ++list->lv_refcount;
+
+	    v = (typval_T *)alloc(sizeof(typval_T));
+	    if (v == NULL)
+		status = FAIL;
+	    else
+	    {
+		/* add the value in advance to allow handling of self-referencial
+		 * data structures */
+		typval_T    *visited_tv = (typval_T *)alloc(sizeof(typval_T));
+		copy_tv(tv, visited_tv);
+		scheme_hash_set(visited, obj, (Scheme_Object *)visited_tv);
+
+		if (SCHEME_VECTORP(obj))
+		{
+		    for (i = 0; i < SCHEME_VEC_SIZE(obj); ++i)
+		    {
+			cval = SCHEME_VEC_ELS(obj)[i];
+			status = mzscheme_to_vim(cval, v, depth + 1, visited);
+			if (status == FAIL)
+			    break;
+			status = list_append_tv(list, v);
+			clear_tv(v);
+			if (status == FAIL)
+			    break;
+		    }
+		}
+		else if (SCHEME_PAIRP(obj) || SCHEME_MUTABLE_PAIRP(obj))
+		{
+		    for (curr = obj;
+			    SCHEME_PAIRP(curr) || SCHEME_MUTABLE_PAIRP(curr);
+			    curr = SCHEME_CDR(curr))
+		    {
+			cval = SCHEME_CAR(curr);
+			status = mzscheme_to_vim(cval, v, depth + 1, visited);
+			if (status == FAIL)
+			    break;
+			status = list_append_tv(list, v);
+			clear_tv(v);
+			if (status == FAIL)
+			    break;
+		    }
+		    /* impoper list not terminated with null
+		     * need to handle the last element */
+		    if (status == OK && !SCHEME_NULLP(curr))
+		    {
+			status = mzscheme_to_vim(cval, v, depth + 1, visited);
+			if (status == OK)
+			{
+			    status = list_append_tv(list, v);
+			    clear_tv(v);
+			}
+		    }
+		}
+		/* nothing to do for scheme_null */
+		vim_free(v);
+	    }
+	    MZ_GC_UNREG();
+	}
+    }
+    else if (SCHEME_HASHTP(obj))
+    {
+	int		i;
+	dict_T		*dict;
+	Scheme_Object   *key = NULL;
+	Scheme_Object   *val = NULL;
+
+	MZ_GC_DECL_REG(2);
+	MZ_GC_VAR_IN_REG(0, key);
+	MZ_GC_VAR_IN_REG(1, val);
+	MZ_GC_REG();
+
+	dict = dict_alloc();
+	if (dict == NULL)
+	    status = FAIL;
+	else
+	{
+	    typval_T    *visited_tv = (typval_T *)alloc(sizeof(typval_T));
+
+	    tv->v_type = VAR_DICT;
+	    tv->vval.v_dict = dict;
+	    ++dict->dv_refcount;
+
+	    copy_tv(tv, visited_tv);
+	    scheme_hash_set(visited, obj, (Scheme_Object *)visited_tv);
+
+	    for (i = 0; i < ((Scheme_Hash_Table *)obj)->size; ++i)
+	    {
+		if (((Scheme_Hash_Table *) obj)->vals[i] != NULL)
+		{
+		    /* generate item for `diplay'ed Scheme key */
+		    dictitem_T  *item = dictitem_alloc((char_u *)string_to_line(
+				((Scheme_Hash_Table *) obj)->keys[i]));
+		    /* convert Scheme val to Vim and add it to the dict */
+		    if (mzscheme_to_vim(((Scheme_Hash_Table *) obj)->vals[i],
+				    &item->di_tv, depth + 1, visited) == FAIL
+			    || dict_add(dict, item) == FAIL)
+		    {
+			dictitem_free(item);
+			status = FAIL;
+			break;
+		    }
+		}
+
+	    }
+	}
+	MZ_GC_UNREG();
+    }
+    else
+    {
+	/* `display' any other value to string */
+	tv->v_type = VAR_STRING;
+	tv->vval.v_string = (char_u *)string_to_line(obj);
+    }
+    return status;
+}
+
+    void
+do_mzeval(char_u *str, typval_T *rettv)
+{
+    int i;
+    Scheme_Object	*ret = NULL;
+    Scheme_Hash_Table	*visited = NULL;
+
+    MZ_GC_DECL_REG(2);
+    MZ_GC_VAR_IN_REG(0, ret);
+    MZ_GC_VAR_IN_REG(0, visited);
+    MZ_GC_REG();
+
+    if (mzscheme_init())
+    {
+	MZ_GC_UNREG();
+	return;
+    }
+
+    MZ_GC_CHECK();
+    visited = scheme_make_hash_table(SCHEME_hash_ptr);
+    MZ_GC_CHECK();
+
+    if (eval_with_exn_handling(str, do_eval, &ret) == OK)
+	mzscheme_to_vim(ret, rettv, 1, visited);
+
+    for (i = 0; i < visited->size; ++i)
+    {
+	/* free up remembered objects */
+	if (visited->vals[i] != NULL)
+	{
+	    free_tv((typval_T *)visited->vals[i]);
+	}
+    }
+
+    MZ_GC_UNREG();
+}
 #endif
 
 /*
--- a/src/proto/eval.pro
+++ b/src/proto/eval.pro
@@ -47,10 +47,14 @@ void list_unref __ARGS((list_T *l));
 void list_free __ARGS((list_T *l, int recurse));
 dictitem_T *dict_lookup __ARGS((hashitem_T *hi));
 char_u *list_find_str __ARGS((list_T *l, long idx));
+int list_append_tv __ARGS((list_T *l, typval_T *tv));
 int list_append_dict __ARGS((list_T *list, dict_T *dict));
 int list_append_string __ARGS((list_T *l, char_u *str, int len));
 int garbage_collect __ARGS((void));
 dict_T *dict_alloc __ARGS((void));
+dictitem_T *dictitem_alloc __ARGS((char_u *key));
+void dictitem_free __ARGS((dictitem_T *item));
+int dict_add __ARGS((dict_T *d, dictitem_T *item));
 int dict_add_nr_str __ARGS((dict_T *d, char *key, long nr, char_u *str));
 char_u *get_dict_string __ARGS((dict_T *d, char_u *key, int save));
 long get_dict_number __ARGS((dict_T *d, char_u *key));
@@ -77,6 +81,7 @@ char_u *get_var_value __ARGS((char_u *na
 void new_script_vars __ARGS((scid_T id));
 void init_var_dict __ARGS((dict_T *dict, dictitem_T *dict_var));
 void vars_clear __ARGS((hashtab_T *ht));
+void copy_tv __ARGS((typval_T *from, typval_T *to));
 void ex_echo __ARGS((exarg_T *eap));
 void ex_echohl __ARGS((exarg_T *eap));
 void ex_execute __ARGS((exarg_T *eap));
--- a/src/proto/if_mzsch.pro
+++ b/src/proto/if_mzsch.pro
@@ -15,4 +15,5 @@ void mzvim_reset_timer __ARGS((void));
 void *mzvim_eval_string __ARGS((char_u *str));
 int mzthreads_allowed __ARGS((void));
 void mzscheme_main __ARGS((void));
+void do_mzeval __ARGS((char_u *str, typval_T *rettv));
 /* vim: set ft=c : */
--- a/src/testdir/Make_dos.mak
+++ b/src/testdir/Make_dos.mak
@@ -29,7 +29,7 @@ SCRIPTS =	test3.out test4.out test5.out 
 		test42.out test52.out test65.out test66.out test67.out \
 		test68.out test69.out
 
-SCRIPTS32 =	test50.out
+SCRIPTS32 =	test50.out test70.out
 
 SCRIPTS_GUI = test16.out
 
--- a/src/testdir/Make_ming.mak
+++ b/src/testdir/Make_ming.mak
@@ -48,7 +48,7 @@ SCRIPTS =	test3.out test4.out test5.out 
 		test42.out test52.out test65.out test66.out test67.out \
 		test68.out test69.out
 
-SCRIPTS32 =	test50.out
+SCRIPTS32 =	test50.out test70.out
 
 SCRIPTS_GUI = test16.out
 
@@ -78,6 +78,7 @@ clean:
 	-$(DEL) small.vim
 	-$(DEL) tiny.vim
 	-$(DEL) mbyte.vim
+	-$(DEL) mzscheme.vim
 	-$(DEL) X*
 	-$(DEL) viminfo
 
--- a/src/testdir/Makefile
+++ b/src/testdir/Makefile
@@ -23,7 +23,7 @@ SCRIPTS = test1.out test2.out test3.out 
 		test54.out test55.out test56.out test57.out test58.out \
 		test59.out test60.out test61.out test62.out test63.out \
 		test64.out test65.out test66.out test67.out test68.out \
-		test69.out
+		test69.out test70.out
 
 SCRIPTS_GUI = test16.out
 
@@ -44,10 +44,10 @@ report:
 $(SCRIPTS) $(SCRIPTS_GUI): $(VIMPROG)
 
 clean:
-	-rm -rf *.out *.failed *.rej *.orig test.log tiny.vim small.vim mbyte.vim test.ok X* valgrind.pid* viminfo
+	-rm -rf *.out *.failed *.rej *.orig test.log tiny.vim small.vim mbyte.vim mzscheme.vim test.ok X* valgrind.pid* viminfo
 
 test1.out: test1.in
-	-rm -f $*.failed tiny.vim small.vim mbyte.vim test.ok X* viminfo
+	-rm -f $*.failed tiny.vim small.vim mbyte.vim mzscheme.vim test.ok X* viminfo
 	$(VALGRIND) $(VIMPROG) -u unix.vim -U NONE --noplugin -s dotest.in $*.in
 	@/bin/sh -c "if diff test.out $*.ok; \
 		then mv -f test.out $*.out; \
--- a/src/testdir/main.aap
+++ b/src/testdir/main.aap
@@ -32,11 +32,11 @@ gui:	newlog $Scripts $ScriptsGUI
 $Scripts $ScriptsGUI: $VimProg
 
 clean:
-	:del {r}{force} *.out test.log tiny.vim small.vim mbyte.vim test.ok X*
+	:del {r}{force} *.out test.log tiny.vim small.vim mbyte.vim mzscheme.vim test.ok X*
 
 # test1 is special, it checks for features
 test1.out: test1.in
-	:del {force} test1.failed tiny.vim small.vim mbyte.vim
+	:del {force} test1.failed tiny.vim small.vim mbyte.vim mzscheme.vim
 	:sys {i} $VimProg -u unix.vim -U NONE --noplugin -s dotest.in test1.in
 	@if os.system("diff test.out test1.ok") != 0:
 		:error test1 FAILED - Something basic is wrong
--- a/src/testdir/test1.in
+++ b/src/testdir/test1.in
@@ -13,6 +13,7 @@ set like small.vim above.  tiny.vim is s
 
 If Vim was not compiled with the +multi_byte feature, the mbyte.vim script will be set like small.vim above.  mbyte.vim is sourced by tests that require the
 +multi_byte feature.
+Similar logic is applied to the +mzscheme feature, using mzscheme.vim.
 
 STARTTEST
 :" Write a single line to test.out to check if testing works at all.
@@ -25,8 +26,11 @@ ae! test.ok
 w! test.out
 qa!
 :w! mbyte.vim
+:w! mzscheme.vim
 :" If +multi_byte feature supported, make mbyte.vim empty.
 :if has("multi_byte") | sp another | w! mbyte.vim | q | endif
+:" If +mzscheme feature supported, make mzscheme.vim empty.
+:if has("mzscheme") | sp another | w! mzscheme.vim | q | endif
 :" If +eval feature supported quit here, leaving tiny.vim and small.vim empty.
 :" Otherwise write small.vim to skip the test.
 :if 1 | q! | endif
new file mode 100644
--- /dev/null
+++ b/src/testdir/test70.in
@@ -0,0 +1,53 @@
+Smoke test for MzScheme interface and mzeval() function
+
+STARTTEST
+:so mzscheme.vim
+:set nocompatible viminfo+=nviminfo
+:function! MzRequire()
+:redir => l:mzversion
+:mz (version)
+:redir END
+:if strpart(l:mzversion, 1, 1) < "4"
+:" MzScheme versions < 4.x:
+:mz (require (prefix vim- vimext))
+:else
+:" newer versions:
+:mz (require (prefix-in vim- 'vimext))
+:mz (require r5rs)
+:endif
+:endfunction
+:silent call MzRequire()
+:mz (define l '("item0" "dictionary with list OK" "item2"))
+:mz (define h (make-hash))
+:mz (hash-set! h "list" l)
+/^1
+:" change buffer contents
+:mz (vim-set-buff-line (vim-eval "line('.')") "1 changed line 1")
+:" scalar test
+:let tmp_string = mzeval('"string"')
+:let tmp_1000 = mzeval('1000')
+:if tmp_string . tmp_1000 == "string1000"
+:let scalar_res = "OK"
+:else
+:let scalar_res = "FAILED"
+:endif
+:call append(search("^1"), "scalar test " . scalar_res)
+:" dictionary containing a list
+:let tmp = mzeval("h")["list"][1]
+:/^2/put =tmp
+:" circular list (at the same time test lists containing lists)
+:mz (set-car! (cddr l) l)
+:let l2 = mzeval("h")["list"]
+:if l2[2] == l2
+:let res = "OK"
+:else
+:let res = "FAILED"
+:endif
+:call setline(search("^3"), "circular test " . res)
+:?^1?,$w! test.out
+:qa!
+ENDTEST
+
+1 line 1
+2 line 2
+3 line 3
new file mode 100644
--- /dev/null
+++ b/src/testdir/test70.ok
@@ -0,0 +1,5 @@
+1 changed line 1
+scalar test OK
+2 line 2
+dictionary with list OK
+circular test OK
--- a/src/version.c
+++ b/src/version.c
@@ -682,6 +682,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    336,
+/**/
     335,
 /**/
     334,