[Nickle]Prototype implementation of twixt in nickle

Keith Packard keithp@keithp.com
Sun, 04 Mar 2001 15:42:36 -0800


This is a multipart MIME message.

--==_Exmh_-3164320800
Content-Type: text/plain; charset=us-ascii


Here's a prototype of how continuations and twixt blocks work together

To recap the syntax:

twixt (enter; leave) body [ else failure ]

        If 'enter' returns True, execute 'body' followed by 'leave'
        else execute 'failure'

        Ensure that any flow of control transfer from inside 'body' to
        outside the twixt passes through 'leave'

        Ensure that any flow of control transfer from outside 'twixt' to
        inside 'body' passes through 'enter'

The basic plan is:
        Jumping from 'l' to 'e'
        Find deepest common parent 'p' of 'l' and 'e' in the twixt chain
        'leave' all twixt blocks between 'l' and 'p'
        'enter' all twixt blocks between 'p' and 'e'
        Pick up execution at 'e'

One complicating factor -- 'enter' blocks may fail, at which point the
'else' clause in the twixt is executed and execution picks up from there.

First-class continuations really help out here.

-keith


--==_Exmh_-3164320800
Content-Type: text/plain ; name="twixt.5c"; charset=us-ascii
Content-Description: twixt.5c
Content-Disposition: attachment; filename="twixt.5c"

typedef struct {
    poly		    next;
    integer		    depth;
    integer(continuation c) enter;
    poly(continuation c)    leave;
} chain;

typedef struct {
    continuation    cont;
    *chain	    list;
} do_continue;

*chain	list;

do_continue	nested;

function chain_depth (*chain c)
{
    if (c)
	return c->depth;
    else
	return 0;
}

function do_jump (do_continue enter, poly value)
{
    *chain		l, e;
    *chain		l_parent, e_parent;
    continuation	c;
    integer		i;
    printf ("do_jump\n");

    l = list;
    e = enter.list;
    l_parent = l;
    e_parent = e;
    /*
     * Make both lists the same length
     */
    i = chain_depth(l) - chain_depth (e);
    if (i >= 0)
	while (i-- > 0)
	    l_parent = l_parent->next;
    else
	while (i++ < 0)
	    e_parent = e_parent->next;
    /*
     * Now find the common parent
     */
    while (l_parent != e_parent)
    {
	l_parent = l_parent->next;
	e_parent = e_parent->next;
    }
    
    /*
     * Unwind the twixt blocks in the source list
     */
    function do_leave (*chain l)
    {
	if (l != l_parent)
	{
	    if (set_jump (&c, 0) == 0)
	    {
		printf ("calling leave\n");
		l->leave (c);
	    }
	    do_leave (l->next);
	}
    }
    
    /*
     * Rewind the twixt blocks in the target list
     */
    function do_enter (*chain  e)
    {
	if (e != e_parent)
	{
	    do_enter (e->next);
	    if (set_jump (&c, 0) == 0)
	    {
		printf ("calling enter\n");
		e->enter (c);
	    }
	}
    }

    do_leave (l);
    do_enter (e);

    long_jump (enter.cont, value);
}

poly function do_set (*do_continue target, poly value)
{
    *target = do_continue { list = list };
    return set_jump (&target->cont, value);
}

function do_twixt (integer () enter, poly() leave, 
		   poly() body, poly() else_)
{
    continuation	c_enter;
    continuation	c_leave;
    continuation	c_else;
    chain		l;

    integer function f_enter (continuation c)
    {
	printf ("f_enter\n");
	if (enter())
	{
	    printf ("f_enter jumping normal\n");
	    long_jump (c, 1);
	}
	else
	{
	    printf ("f_enter jumping fail\n");
	    long_jump (c_else, 1);
	}
    }

    function f_leave (continuation c)
    {
	printf ("f_leave\n");
	leave();
	long_jump (c, 1);
    }

    if (set_jump (&c_else, 0) == 0)
    {
	if (set_jump (&c_enter, 0) == 0)
	{
	    f_enter (c_enter);
	}
	else if (set_jump (&c_leave, 0) == 0)
	{
	    chain	l;
    
	    l = chain { next = list, enter = f_enter, leave = f_leave };
	    if (list)
		l.depth = list->depth + 1;
	    else
		l.depth = 1;
	    list = &l;
	    printf ("before body\n");
	    body ();
	    printf ("after body\n");
	    f_leave (c_leave);
	}
    }
    else
    {
        printf ("else\n");
        else_();
    }
}

function body (do_continue jump)
{
    printf ("body\n");
    if (do_set (&nested, 0) == 0)
    {
	printf ("jumping\n");
	do_jump (jump, 1);
    }
    else
    {
	printf ("re-entered body\n");
	printf ("returning\n");
    }
}

integer	critical_value = 1;

function critical (do_continue jump)
{
    printf ("critical enter\n");
    do_twixt (integer func () 
	      {
		printf ("enter lambda\n");
		return critical_value;
	      },
	      func ()
	      {
		printf ("leave lambda\n");
	      },
	      func ()
	      {
		printf ("body lambda\n");
		body (jump);
	      },
	      func ()
	      {
		printf ("else lambda\n");
	      });
    
    printf ("critical done\n");
}

function start ()
{
    do_continue    c;

    printf ("start\n");
    if (do_set (&c, 0) == 0)
    {
	critical (c);
	printf ("back from critical\n");
    }
    else
    {
	printf ("back from do_jump\n");
    }
    printf ("start done\n");
}

--==_Exmh_-3164320800--