genius r647 - in trunk: . help/C src



Author: jirka
Date: Mon Feb 25 00:35:46 2008
New Revision: 647
URL: http://svn.gnome.org/viewvc/genius?rev=647&view=rev

Log:

Sun Feb 24 18:35:45 2008  Jiri (George) Lebl <jirka 5z com>

	* src/calc.c: print extra dict of a function by printing a list
	  of variable assignments before the function body.

	* src/dict.c: a little cleanup and fix the problem with subst list
	  updating and d_replacefunc which happened when a local variable
	  which was previously set to something else was set to a function.

	* src/geniustests.txt: add more tests on returning functions
	  and the subst list handling

	* help/C/genius.xml: add section on returning functions from
	  functions



Modified:
   trunk/ChangeLog
   trunk/NEWS
   trunk/TODO
   trunk/help/C/genius.txt
   trunk/help/C/genius.xml
   trunk/src/calc.c
   trunk/src/dict.c
   trunk/src/geniustests.txt

Modified: trunk/NEWS
==============================================================================
--- trunk/NEWS	(original)
+++ trunk/NEWS	Mon Feb 25 00:35:46 2008
@@ -6,7 +6,9 @@
 * QuadraticFormula built in and more numerically stable
 * CHANGE: It's Fibonacci in correct spelling, short name is still fib
 * Calling internal functions is now slightly faster
+* Fix crash related to returning custom functions from functions
 * Fix some memory leaks
+* Documentation updates
 * Translations (FIXME)
 
 Changes to 1.0.2

Modified: trunk/TODO
==============================================================================
--- trunk/TODO	(original)
+++ trunk/TODO	Mon Feb 25 00:35:46 2008
@@ -5,15 +5,6 @@
   with context > 0 on the toplevel context.   It's strange.  And I can't
   repro it now
 
-* crash:
-  function f(x) = (k=1; ff=`()=(k+1) ; k=(`()=0) ; ff); ll=f(5)
-
-* behaviour?:
-  function f(x) = (k=1; ff=`()=(k+1) ; k=null ; ff)
-  the returned function should return 2 not "((null)+1)" shouldn't it?
-  That is, replacement (putting into extra_dict)
-  should happen on function definition, not on context pop.
-
 * Implement max_nodes in the command line version
   * Implement MaxNodes parameter
 

Modified: trunk/help/C/genius.txt
==============================================================================
--- trunk/help/C/genius.txt	(original)
+++ trunk/help/C/genius.txt	Mon Feb 25 00:35:46 2008
@@ -12,7 +12,7 @@
 
     <kaiw itee uq edu au> 
 
-   Copyright (c) 1997-2007 Jiri (George) Lebl
+   Copyright (c) 1997-2008 Jiri (George) Lebl
 
    Copyright (c) 2004 Kai Willadsen
 
@@ -167,9 +167,11 @@
 
                 7.2. Toplevel Syntax
 
-                7.3. GEL Startup Procedure
+                7.3. Global Variables and Scope of Variables
 
-                7.4. Loading Programs
+                7.4. GEL Startup Procedure
+
+                7.5. Loading Programs
 
    8. Matrices in GEL
 
@@ -1564,7 +1566,52 @@
 
      ----------------------------------------------------------------------
 
-7.3. GEL Startup Procedure
+7.3. Global Variables and Scope of Variables
+
+   It is possible to return functions as value. This way you can build
+   functions which construct special purpose functions according to some
+   parameters. The tricky bit is what variables does the function see. The
+   way this works in GEL is that when a function returns another function,
+   all identifiers referenced in the function body are prepended a private
+   dictionary of the returned function. So the function will see all
+   variables that were in scope when it was defined. For example we define a
+   function which returns a function which adds 5 to its argument.
+
+ function f() = (
+   k = 5;
+   `(x) = (x+k)
+ )
+
+   Notice that the function adds k to x. You could use this as follows.
+
+ g = f();
+ g(5)
+
+   And g(5) should return 10.
+
+   One thing to note is that the value of k that is used is the one that's in
+   effect when the f returns. So for example
+
+ function f() = (
+   k = 5;
+   r = `(x) = (x+k);
+   k = 10;
+   r
+ )
+
+   will return a function that adds 10 to its argument rather than 5. This is
+   because the extra dictionary is created only when the context in which the
+   function was defined ends, which is when the function f returns. This is
+   consistent with how you would expect the function r to work inside the
+   function f according to the rules of scope of variables in GEL. Only those
+   variables are added to the extra dictionary that are in the context that
+   just ended and no longer exists. Variables used in the function that are
+   in still valid contexts will work as usual, using the current value of the
+   variable.
+
+     ----------------------------------------------------------------------
+
+7.4. GEL Startup Procedure
 
    First the program looks for the installed library file (the compiled
    version lib.cgel) in the installed directory, then it looks into the
@@ -1576,7 +1623,7 @@
 
      ----------------------------------------------------------------------
 
-7.4. Loading Programs
+7.5. Loading Programs
 
    Sometimes you have a larger program that you wrote into a file and want to
    read in that file. In these situations, you have two options. You can keep
@@ -1921,6 +1968,14 @@
 
            Check if argument is a boolean (and not a number).
 
+   IsDefined
+
+ IsDefined (id)
+
+           Check if an id is defined. You should pass a string or and
+           identifier. If you pass a matrix, each entry will be evaluated
+           separately and the matrix should contain strings or identifiers.
+
    IsFunction
 
  IsFunction (arg)
@@ -2079,6 +2134,14 @@
 
            The true boolean value.
 
+   undefine
+
+ undefine (id)
+
+           Undefine a variable. This includes locals and globals, every value
+           on all context levels is wiped. This function should really not be
+           used on local variables.
+
    unprotect
 
  unprotect (id)

Modified: trunk/help/C/genius.xml
==============================================================================
--- trunk/help/C/genius.xml	(original)
+++ trunk/help/C/genius.xml	Mon Feb 25 00:35:46 2008
@@ -1990,6 +1990,55 @@
       </para>
     </sect1>
 
+     <sect1 id="genius-gel-returning-functions">
+       <title>Global Variables and Scope of Variables</title>
+	<para>
+	  It is possible to return functions as value.  This way you can
+	  build functions which construct special purpose functions according
+	  to some parameters.  The tricky bit is what variables does the
+	  function see.  The way this works in GEL is that when a function
+	  returns another function, all identifiers referenced in the
+	  function body are prepended a private dictionary of the returned
+	  function.  So the function will see all variables that were in scope
+	  when it was defined.  For example we define a function which
+	  returns a function which adds 5 to its argument.
+<programlisting>function f() = (
+  k = 5;
+  `(x) = (x+k)
+)
+</programlisting>
+	  Notice that the function adds <varname>k</varname> to
+	  <varname>x</varname>.  You could use this as follows.
+<programlisting>g = f();
+g(5)
+</programlisting>
+	  And <userinput>g(5)</userinput> should return 10.
+        </para>
+	<para>
+	  One thing to note is that the value of <varname>k</varname>
+	  that is used is the one that's in effect when the
+	  <function>f</function> returns.  So for example
+<programlisting>function f() = (
+  k = 5;
+  r = `(x) = (x+k);
+  k = 10;
+  r
+)
+</programlisting>
+	  will return a function that adds 10 to its argument rather than 5.
+	  This is because the extra dictionary is created only when the context
+	  in which the function was defined ends, which is when the function
+	  <function>f</function> returns.  This is consistent with how you
+	  would expect the function <function>r</function> to work inside
+	  the function <function>f</function> according to the rules of
+	  scope of variables in GEL.  Only those variables are added to the
+	  extra dictionary that are in the context that just ended and
+	  no longer exists.  Variables
+	  used in the function that are in still valid contexts will work
+	  as usual, using the current value of the variable.
+	</para>
+    </sect1>
+
     <sect1 id="genius-gel-startup-procedure">
       <title>GEL Startup Procedure</title>
       <para>

Modified: trunk/src/calc.c
==============================================================================
--- trunk/src/calc.c	(original)
+++ trunk/src/calc.c	Mon Feb 25 00:35:46 2008
@@ -1252,6 +1252,64 @@
 	return FALSE;
 }
 
+static void
+append_func (GelOutput *gelo, GelEFunc *f)
+{
+	GSList *li;
+
+	if G_UNLIKELY (f == NULL) {
+		gel_errorout (_("NULL function!"));
+		gel_output_string(gelo,"(?)");
+		return;
+	}
+	if(f->type==GEL_BUILTIN_FUNC) {
+		gel_output_string(gelo,"(<builtin function>)");
+		return;
+	}
+
+	gel_output_string(gelo,"(`(");
+
+	for(li=f->named_args; li!=NULL; li=g_slist_next(li)) {
+		GelToken *id = li->data;
+		if (li != f->named_args)
+			gel_output_string (gelo, ",");
+		gel_output_string(gelo,id->token);
+	}
+
+	if (f->vararg)
+		gel_output_string (gelo, "...");
+
+	if G_LIKELY (f->type==GEL_USER_FUNC) {
+		gel_output_string(gelo,")=");
+		D_ENSURE_USER_BODY (f);
+		if (f->extra_dict != NULL) {
+			GSList *li;
+			gel_output_string(gelo,"(");
+			for (li = f->extra_dict; li != NULL; li = li->next) {
+				GelEFunc *ff = li->data;
+				gel_output_string (gelo, ff->id->token);
+				gel_output_string (gelo, "=");
+				if (ff->type == GEL_VARIABLE_FUNC) {
+					gel_print_etree (gelo, ff->data.user, FALSE);
+				} else {
+					append_func (gelo, ff);
+				}
+				gel_output_string (gelo, ";");
+			}
+		}
+		gel_print_etree (gelo, f->data.user, FALSE);
+		if (f->extra_dict != NULL) {
+			gel_output_string(gelo,")");
+		}
+		gel_output_string(gelo,")");
+	} else {
+		/*variable and reference functions should
+		  never be in the etree*/
+		gel_errorout (_("Unexpected function type!"));
+		gel_output_string(gelo,")(?))");
+	}
+}
+
 /*make a string representation of an expression*/
 void
 gel_print_etree (GelOutput *gelo,
@@ -1340,46 +1398,8 @@
 		gel_output_string(gelo,"\"");
 		break;
 	case FUNCTION_NODE:
-		{
-			GSList *li;
-			GelEFunc *f;
-			
-			f = n->func.func;
-			if G_UNLIKELY (f == NULL) {
-				gel_errorout (_("NULL function!"));
-				gel_output_string(gelo,"(?)");
-				break;
-			}
-			if(f->type==GEL_BUILTIN_FUNC) {
-				gel_output_string(gelo,"(<builtin function>)");
-				break;
-			}
-
-			gel_output_string(gelo,"(`(");
-
-			for(li=f->named_args; li!=NULL; li=g_slist_next(li)) {
-				GelToken *id = li->data;
-				if(li!=f->named_args)
-					gel_output_string(gelo,",");
-				gel_output_string(gelo,id->token);
-			}
-
-			if (f->vararg)
-				gel_output_string (gelo, "...");
-
-			if G_LIKELY (f->type==GEL_USER_FUNC) {
-				gel_output_string(gelo,")=");
-				D_ENSURE_USER_BODY (f);
-				gel_print_etree (gelo, f->data.user, FALSE);
-				gel_output_string(gelo,")");
-			} else {
-				/*variable and reference functions should
-				  never be in the etree*/
-				gel_errorout (_("Unexpected function type!"));
-				gel_output_string(gelo,")(?))");
-			}
-			break;
-		}
+		append_func (gelo, n->func.func);
+		break;
 	case COMPARISON_NODE:
 		appendcomp(gelo,n);
 		break;

Modified: trunk/src/dict.c
==============================================================================
--- trunk/src/dict.c	(original)
+++ trunk/src/dict.c	Mon Feb 25 00:35:46 2008
@@ -499,11 +499,7 @@
 {
 	GSList *li;
 	for (li = context.stack; li != NULL; li = li->next) {
-		GSList *fl = g_slist_find (li->data, func);
-		if (fl != NULL) {
-			li->data = g_slist_delete_link (li->data, fl);
-			return;
-		}
+		li->data = g_slist_remove (li->data, func);
 	}
 }
 
@@ -556,7 +552,7 @@
 }
 
 static void
-whack_from_lists (GelEFunc *func)
+whack_from_subst_lists (GelEFunc *func)
 {
 	GSList *li;
 	for (li = context.subststack; li != NULL; li = li->next) {
@@ -576,7 +572,7 @@
 		/* On a lower stackframe? */
 		/* weird but true.  So whack it and put it here,
 		 * it will get to the lower one eventually */
-		whack_from_lists (func);
+		whack_from_subst_lists (func);
 	}
 	context.subststack->data = 
 		g_slist_prepend (context.subststack->data, func);
@@ -594,7 +590,7 @@
 	*/
 
 	if (n->on_subst_list) {
-		whack_from_lists (n);
+		whack_from_subst_lists (n);
 		n->on_subst_list = 0;
 	}
 
@@ -628,14 +624,22 @@
 d_replacefunc(GelEFunc *old,GelEFunc *_new)
 {
 	GSList *li;
-
-	/* assert some things we don't deal with.  Should we? */
-	g_assert ( ! _new->on_subst_list);
-	g_assert ( ! old->on_subst_list);
+	gboolean put_on_subst = FALSE;
 
 	g_return_if_fail(old && _new);
 	g_return_if_fail(old->id == _new->id);
 
+	if (old->on_subst_list) {
+		whack_from_subst_lists (old);
+		old->on_subst_list = 0;
+	}
+
+	if (_new->on_subst_list) {
+		whack_from_subst_lists (_new);
+		_new->on_subst_list = 0;
+		put_on_subst = TRUE;
+	}
+
 	if(old->type == GEL_USER_FUNC ||
 	   old->type == GEL_VARIABLE_FUNC)
 		gel_freetree(old->data.user);
@@ -650,6 +654,11 @@
 
 	memcpy(old,_new,sizeof(GelEFunc));
 
+	/* FIXME: this is inefficient */
+	if (put_on_subst) {
+		d_put_on_subst_list (old);
+	}
+
 #ifndef MEM_DEBUG_FRIENDLY
 	/*prepend to free list*/
 	_new->data.next = free_funcs;

Modified: trunk/src/geniustests.txt
==============================================================================
--- trunk/src/geniustests.txt	(original)
+++ trunk/src/geniustests.txt	Mon Feb 25 00:35:46 2008
@@ -889,7 +889,15 @@
 IsDefined("IsDefined")						true
 IsDefined(`IsDefined)						true
 IsDefined(`UguUguUgu)						false
+undefine(`UguUguUgu)+1						((null)+1)
 a=9;undefine(`a);a						a
 a=9;undefine("a");a						a
+function f(x)=(k=1;ff=`()=(k+1);k=(`()=0);ff);ll=f(5)		(`()=(k=(`()=0);(k+1)))
+function f(x)=(k=1;ff=`()=(k+1);k=2;ff);ll=f(5)			(`()=(k=2;(k+1)))
+function f(x)=(k=1;ff=`()=(k+1);k=2;ff);ll=f(5);ll()		3
+function f(x)=(k=1;(function ff()=(k+1));k=2;ff);ll=f(5);ll()	3
+function f(x)=(ff=`()=(k+1);ff);ll=f(5)				(`()=(k+1))
+k=9;function f(x)=(ff=`()=(k+1);ff);ll=f(5)			(`()=(k+1))
+k=9;function f(x)=(ff=`()=(k+1);ff);ll=f(5);ll()		10
 load "nullspacetest.gel"					true
 load "longtest.gel"						true



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]