# New Ticket Created by  Jonathan Sillito 
# Please include the string:  [perl #16797]
# in the subject line of all future correspondence about this issue. 
# <URL: http://rt.perl.org/rt2/Ticket/Display.html?id=16797 >


This patch supersedes patch [perl #16087], which will not apply
correctly, since key stuff has changed.

I realize Dan has not given official word on how lexicals/closures are
to work yet (unless I missed it?), but I thought I would implement some
stuff anyway. I don't mind if I have to rework it when Dan gets to this
(probably not until sometime after the 0.0.8 release, I suppose?).

Anyway, this patch has two main goals. The first is to give scratchpads
a pointer to their parent pad. In the process I added a Scratchpad pmc
that uses its data pointer to point to a PerlHash (temporarily?) and
uses its cache pointer to point to its parent. I am not sure about this
use of the cache pointer, but it is only used internally so it should be
easy to change.

The scratchpad.pmc file is attached.

The second goal is to make subs closures. Below is an example of how
this works. You will notice that at the end of each sub there is an
extra 'pop_pad', this is to remove the pad that is pushed on the
pad_stack when the sub is invoked (or should the ret op take care of
this?).

    new_pad
    new P0, .Sub
    set_addr I3, get_closure
    set P0, I3
    invoke
   
    # move returned sub pmc to P0, then invoke twice
    set P0, P5
    # invoke puts correct scratch pad on top of stack
    invoke
    invoke

    find_lex P6, "a"
    print P6 # should be undef which prints as ''
    print "\n"
    pop_pad
    end

# a sub that returns a sub (closure)
get_closure:
    new_pad
    print "called get_closure\n"
    new P10, .PerlInt
    set P10, 57 
    store_lex "a", P10

    # sub grabs current current pad when created
    new P5, .Sub    
    set_addr I5, closure
    set P5, I5

    # returning sub pmc in first return pmc register,
    # which is P5
    set I5, 1
    pop_pad
    pop_pad # extra pad needs to be poped
    ret
closure:
    print "called closure\n"
    find_lex P2, "a"
    print P2
    print "\n"
    pop_pad # extra pad needs to be poped
    ret

produces the output:

    called get_closure
    called closure
    57
    called closure
    57


The attached file closure.patch has the following effects:

- changes lexical ops in core.ops to use Scratchpad pmc,
  and to use keys properly.
- adds Scratchpad to enum in include/parrot/pmc.h
- adds Parrot_Scratchpad_class_init(enum_class_Scratchpad); 
  to global_setup.c
- adds additional test to t/op/lexicals.t
- adds additional test to t/pmc/sub.t
- fixes examples/assembly/lexical.pasm (reverses PMC and 
  string arguments to store_lex op).
- changes new_sub method in sub.c
- changes invoke in sub.pmc (to put pad in place)

Comments?
--
Jonathan Sillito





-- attachment  1 ------------------------------------------------------
url: http://rt.perl.org/rt2/attach/35844/29037/feda4d/closure.patch

-- attachment  2 ------------------------------------------------------
url: http://rt.perl.org/rt2/attach/35844/29038/d0f8a9/scratchpad.pmc

Index: core.ops
===================================================================
RCS file: /cvs/public/parrot/core.ops,v
retrieving revision 1.203
diff -u -r1.203 core.ops
--- core.ops	23 Aug 2002 17:21:38 -0000	1.203
+++ core.ops	27 Aug 2002 20:47:43 -0000
@@ -3523,8 +3523,8 @@
 =cut
 
 op new_pad() {
-    PMC* hash = pmc_new(interpreter, enum_class_PerlHash);
-    stack_push(interpreter, &interpreter->ctx.pad_stack, hash, STACK_ENTRY_PMC, STACK_CLEANUP_NULL);
+    PMC * pad = pmc_new(interpreter, enum_class_Scratchpad);
+    stack_push(interpreter, &interpreter->ctx.pad_stack, pad, STACK_ENTRY_PMC, STACK_CLEANUP_NULL);
 
     goto NEXT();
 }
@@ -3535,22 +3535,27 @@
 }
 
 op store_lex(in STR, in PMC) {
-    PMC * hash = NULL;
+    PMC * pad;
     PMC * key = key_new_string(interpreter, $1);
-    Stack_entry_type type = NO_STACK_ENTRY_TYPE;
-    hash = (PMC *)stack_peek(interpreter, interpreter->ctx.pad_stack, &type);
-    hash->vtable->set_pmc_keyed(interpreter, hash, key, $2, NULL);
+    Stack_entry_type type = 0;
+    pad = (PMC *)stack_peek(interpreter, interpreter->ctx.pad_stack, &type);
+    pad->vtable->set_pmc_keyed(interpreter, pad, key, $2, NULL);
     goto NEXT();
 }
 
 op find_lex(out PMC, in STR) {
-    PMC * hash = NULL;
+    PMC * pad;
     PMC * key = key_new_string(interpreter, $2);
-    Stack_entry_type type = NO_STACK_ENTRY_TYPE;
-    hash = (PMC *)stack_peek(interpreter, interpreter->ctx.pad_stack, &type);
-    $1 = hash->vtable->get_pmc_keyed(interpreter, hash, key);
-
-    /* FIXME: should the not found case be an internal_exception ? */
+    Stack_entry_type type = 0;
+    pad = (PMC *)stack_peek(interpreter, interpreter->ctx.pad_stack, &type);
+    if (!pad) {
+        /* really a compiler error, I guess */
+        internal_exception(INTERP_ERROR,
+            "No scratchpad to find from (missing new_pad?)");
+    }
+    else {
+        $1 = pad->vtable->get_pmc_keyed(interpreter, pad, key);
+    }
 
     goto NEXT();
 }
Index: global_setup.c
===================================================================
RCS file: /cvs/public/parrot/global_setup.c,v
retrieving revision 1.34
diff -u -r1.34 global_setup.c
--- global_setup.c	19 Aug 2002 23:14:48 -0000	1.34
+++ global_setup.c	27 Aug 2002 20:47:44 -0000
@@ -37,6 +37,7 @@
     Parrot_Continuation_class_init(enum_class_Continuation);
     Parrot_MultiArray_class_init(enum_class_MultiArray);
     Parrot_Key_class_init(enum_class_Key);
+    Parrot_Scratchpad_class_init(enum_class_Scratchpad);
 
     /* Now register the names of the PMCs */
 
Index: sub.c
===================================================================
RCS file: /cvs/public/parrot/sub.c,v
retrieving revision 1.7
diff -u -r1.7 sub.c
--- sub.c	22 Aug 2002 14:24:05 -0000	1.7
+++ sub.c	27 Aug 2002 20:47:44 -0000
@@ -31,10 +31,11 @@
 struct Parrot_Sub *
 new_sub(struct Parrot_Interp * interp, opcode_t * address) 
 {
+    Stack_entry_type type = 0;
     /* Using system memory until I figure out GC issues */
     struct Parrot_Sub * newsub = mem_sys_allocate(sizeof(struct Parrot_Sub));
     newsub->init = address;
-    newsub->lex_pad = NULL;
+    newsub->lex_pad = (struct Scratchpad *)stack_peek(interp, interp->ctx.pad_stack, &type);
     return newsub;
 }
 
Index: include/parrot/pmc.h
===================================================================
RCS file: /cvs/public/parrot/include/parrot/pmc.h,v
retrieving revision 1.38
diff -u -r1.38 pmc.h
--- include/parrot/pmc.h	19 Aug 2002 23:15:52 -0000	1.38
+++ include/parrot/pmc.h	27 Aug 2002 20:47:44 -0000
@@ -30,6 +30,7 @@
     enum_class_CSub,
     enum_class_MultiArray,
     enum_class_Key,
+    enum_class_Scratchpad,
     enum_class_max = 100
 };
 VAR_SCOPE VTABLE Parrot_base_vtables[enum_class_max];
Index: t/op/lexicals.t
===================================================================
RCS file: /cvs/public/parrot/t/op/lexicals.t,v
retrieving revision 1.2
diff -u -r1.2 lexicals.t
--- t/op/lexicals.t	6 Aug 2002 22:42:35 -0000	1.2
+++ t/op/lexicals.t	27 Aug 2002 20:47:44 -0000
@@ -1,6 +1,6 @@
 #! perl -w
 
-use Parrot::Test tests => 2;
+use Parrot::Test tests => 3;
 
 output_is(<<CODE, <<OUTPUT, "simple store and fetch");
 	new_pad
@@ -53,6 +53,27 @@
 0
 2
 0
+OUTPUT
+
+output_is(<<CODE, <<OUTPUT, "undefined lexicals");
+  new P0, .PerlInt
+  set P0, 30
+  
+  # outer most lexical scope
+  new_pad
+
+    # nested lexical scope
+    new_pad
+    store_lex "a", P0
+    pop_pad
+
+  # should be undefined (printing undef does nothing)
+  find_lex P2, "a"
+  print P2
+  print "\\n"
+  end
+CODE
+
 OUTPUT
 
 1;
Index: t/pmc/sub.t
===================================================================
RCS file: /cvs/public/parrot/t/pmc/sub.t,v
retrieving revision 1.2
diff -u -r1.2 sub.t
--- t/pmc/sub.t	6 Aug 2002 22:43:08 -0000	1.2
+++ t/pmc/sub.t	27 Aug 2002 20:47:44 -0000
@@ -1,6 +1,6 @@
 #! perl -w
 
-use Parrot::Test tests => 3;
+use Parrot::Test tests => 4;
 use Test::More;
 
 output_is(<<'CODE', <<'OUTPUT', "PASM subs");
@@ -47,6 +47,53 @@
 0
 1 done
 done 2
+OUTPUT
+
+output_is(<<'CODE', <<'OUTPUT', "PASM closure");
+    new_pad
+    new P0, .Sub
+    set_addr I3, get_closure
+    set P0, I3
+    invoke
+    set P0, P5
+    invoke
+    invoke
+
+    find_lex P6, "a"
+    print P6 # should be undef which prints as nothing
+    print "\n"
+    pop_pad
+    end
+get_closure:
+    new_pad
+    print "called get_closure\n"
+    new P10, .PerlInt
+    set P10, 57 
+    store_lex "a", P10
+    new P5, .Sub
+    set_addr I5, closure
+    set P5, I5
+    set I5, 1   # number of PMCs returned
+end_get_closure:
+    pop_pad
+    pop_pad # extra pad needs to be poped
+    ret
+closure:
+    print "called closure\n"
+    find_lex P2, "a"
+    print P2
+    print "\n"
+end_closure:
+    pop_pad # extra pad needs to be poped
+    ret
+
+CODE
+called get_closure
+called closure
+57
+called closure
+57
+
 OUTPUT
 
 output_is(<<'CODE', <<'OUTPUT', "Continuations");
Index: examples/assembly/lexical.pasm
===================================================================
RCS file: /cvs/public/parrot/examples/assembly/lexical.pasm,v
retrieving revision 1.1
diff -u -r1.1 lexical.pasm
--- examples/assembly/lexical.pasm	31 Jul 2002 02:48:49 -0000	1.1
+++ examples/assembly/lexical.pasm	27 Aug 2002 20:47:44 -0000
@@ -15,14 +15,14 @@
   
 # outer most lexical scope
 new_pad
-store_lex P0, "a"
+store_lex "a", P0
 find_lex P3, "a"
 print P3 # prints 0
 print "\n"
 
 new_pad
-store_lex P1, "b"
-store_lex P1, "a"
+store_lex "b", P1
+store_lex "a", P1
 
 find_lex P3, "a"
 print P3 # prints 1
Index: classes/sub.pmc
===================================================================
RCS file: /cvs/public/parrot/classes/sub.pmc,v
retrieving revision 1.5
diff -u -r1.5 sub.pmc
--- classes/sub.pmc	4 Aug 2002 22:55:03 -0000	1.5
+++ classes/sub.pmc	27 Aug 2002 20:47:44 -0000
@@ -45,9 +45,16 @@
    }
 
    void* invoke (void* next) {
-       /* return address that the interpreter should jump to */
+       /* address to return to */
        stack_push(INTERP, &(INTERP->ctx.control_stack), next,
 		  STACK_ENTRY_DESTINATION, STACK_CLEANUP_NULL);
+
+       /* fix scratchpad */
+       stack_push(INTERP, &(INTERP->ctx.pad_stack), 
+                  ((struct Parrot_Sub*)SELF->data)->lex_pad,
+		  STACK_ENTRY_PMC, STACK_CLEANUP_NULL);
+
+       /* return address that the interpreter should jump to */
        return ((struct Parrot_Sub*)SELF->data)->init;
    } 
 
/* Scratchpad.pmc
 *  Copyright: (When this is determined...it will go here)
 *  CVS Info
 *     $Id$
 *  Overview:
 *     These are the vtable functions for the Scratchpad base class
 *  Data Structure and Algorithms:
 *     SELF->data   stores the lexicals
 *     SELF->cache  points to the parent Scratchpad, if there is one
 *                  and is NULL otherwise
 *  History:
 *     Initial revision by sillito 2002/08/08
 *  Notes:
 *  References:
 */

#include "parrot/parrot.h"

pmclass Scratchpad {

    void init () {
        /* get parent */
        PMC * parent;
        Stack_entry_type type = 0;
        parent = (PMC *)stack_peek(interpreter, interpreter->ctx.pad_stack, &type);
        SELF->cache.pmc_val = parent;

        /* temporarily using a hash to store lexicals */
        SELF->data = pmc_new(interpreter, enum_class_PerlHash);

        SELF->flags |= (PMC_is_PMC_ptr_FLAG | PMC_custom_mark_FLAG);
    }

    void init_pmc (PMC* initializer) {
    }

    void morph (INTVAL type) {
    }

    PMC* mark (PMC* tail) {
        /* 
         * this assumes that the data pointer points to something
         * with a customer mark routine, which hash does ...
         */
        tail = ((PMC *)SELF->data)->vtable->mark(interpreter, 
            SELF->data, tail);
        if (SELF->cache.pmc_val) {
            tail = Parrot_Scratchpad_mark(interpreter, SELF->cache.pmc_val, tail);
        }

        return tail;
    }

    void destroy () {
    }

    INTVAL type () {
        return enum_class_Scratchpad;
    }

    STRING* name () {
        return whoami;
    }

    PMC* get_pmc_keyed (PMC* key) {
        PMC * value;
        value = ((PMC *)SELF->data)->vtable->get_pmc_keyed(interpreter, 
            SELF->data, key);

        if (value->vtable->type(interpreter, value) == enum_class_PerlUndef && 
            SELF->cache.pmc_val) {
            value = SELF->cache.pmc_val->vtable->get_pmc_keyed(interpreter, 
                SELF->cache.pmc_val, key);
        }

        return value;
    }

/* implement this for getting lexicals by position
    PMC* get_pmc_keyed_int (INTVAL* key) {
    }
*/

    void set_pmc_keyed (PMC* key, PMC* value, PMC* value_key) {
        ((PMC *)SELF->data)->vtable->set_pmc_keyed(interpreter, 
            SELF->data, key, value, value_key);
    }

}

Reply via email to