develooper Front page | perl.cvs.parrot | Postings from February 2008

[svn:parrot] r26029 - in trunk: . languages/plumhead/config/makefiles languages/plumhead/pmc languages/plumhead/t/pmc

From:
bernhard
Date:
February 23, 2008 12:43
Subject:
[svn:parrot] r26029 - in trunk: . languages/plumhead/config/makefiles languages/plumhead/pmc languages/plumhead/t/pmc
Message ID:
20080223204329.02FB8CBA13@x12.develooper.com
Author: bernhard
Date: Sat Feb 23 12:43:26 2008
New Revision: 26029

Added:
   trunk/languages/plumhead/pmc/   (props changed)
   trunk/languages/plumhead/pmc/phparray.pmc   (contents, props changed)
   trunk/languages/plumhead/t/pmc/   (props changed)
   trunk/languages/plumhead/t/pmc/array.t   (contents, props changed)
Modified:
   trunk/CREDITS
   trunk/MANIFEST
   trunk/MANIFEST.SKIP
   trunk/languages/plumhead/config/makefiles/root.in

Log:
#51062: [NEW] PHPArray PMC
Add an implementation of PHPArray.

Courtesy of Christoph Otto.


Modified: trunk/CREDITS
==============================================================================
--- trunk/CREDITS	(original)
+++ trunk/CREDITS	Sat Feb 23 12:43:26 2008
@@ -139,6 +139,7 @@
 N: Christoph Otto
 D: Patch for key flags in pdd08
 D: Range check in Env PMC
+D: PHPArray implementation
 E: christoph@mksig.org
 
 N: chromatic

Modified: trunk/MANIFEST
==============================================================================
--- trunk/MANIFEST	(original)
+++ trunk/MANIFEST	Sat Feb 23 12:43:26 2008
@@ -1912,6 +1912,7 @@
 languages/plumhead/lib/Parrot/Test/Plumhead/Yacc.pm         [plumhead]
 languages/plumhead/past_xml.xsd                             [plumhead]
 languages/plumhead/plumhead.pl                              [plumhead]
+languages/plumhead/pmc/phparray.pmc                         [plumhead]
 languages/plumhead/src/antlr3/GenPastPir.g                  [plumhead]
 languages/plumhead/src/antlr3/GenPastPir.java               [plumhead]
 languages/plumhead/src/antlr3/Plumhead.g                    [plumhead]
@@ -1940,6 +1941,7 @@
 languages/plumhead/t/strings.t                              [plumhead]
 languages/plumhead/t/superglobals.t                         [plumhead]
 languages/plumhead/t/variables.t                            [plumhead]
+languages/plumhead/t/pmc/array.t                            [plumhead]
 languages/pugs/config/makefiles/root.in                     [pugs]
 languages/pugs/include/pugs_common.h                        [pugs]
 languages/pugs/pmc/pugsany.pmc                              [pugs]

Modified: trunk/MANIFEST.SKIP
==============================================================================
--- trunk/MANIFEST.SKIP	(original)
+++ trunk/MANIFEST.SKIP	Sat Feb 23 12:43:26 2008
@@ -1,6 +1,6 @@
 # ex: set ro:
 # $Id$
-# generated by tools\dev\mk_manifest_and_skip.pl Sun Feb 10 12:42:56 2008 UT
+# generated by tools/dev/mk_manifest_and_skip.pl Sat Feb 23 20:30:41 2008 UT
 #
 # This file should contain a transcript of the svn:ignore properties
 # of the directories in the Parrot subversion repository. (Needed for
@@ -1008,6 +1008,29 @@
 ^languages/plumhead/plumhead\.pbc/
 ^languages/plumhead/plumhead_pct\.pbc$
 ^languages/plumhead/plumhead_pct\.pbc/
+# generated from svn:ignore of 'languages/plumhead/pmc/'
+^languages/plumhead/pmc/.*\.bundle$
+^languages/plumhead/pmc/.*\.bundle/
+^languages/plumhead/pmc/.*\.dll$
+^languages/plumhead/pmc/.*\.dll/
+^languages/plumhead/pmc/.*\.dump$
+^languages/plumhead/pmc/.*\.dump/
+^languages/plumhead/pmc/.*\.exp$
+^languages/plumhead/pmc/.*\.exp/
+^languages/plumhead/pmc/.*\.h$
+^languages/plumhead/pmc/.*\.h/
+^languages/plumhead/pmc/.*\.ilk$
+^languages/plumhead/pmc/.*\.ilk/
+^languages/plumhead/pmc/.*\.lib$
+^languages/plumhead/pmc/.*\.lib/
+^languages/plumhead/pmc/.*\.manifest$
+^languages/plumhead/pmc/.*\.manifest/
+^languages/plumhead/pmc/.*\.obj$
+^languages/plumhead/pmc/.*\.obj/
+^languages/plumhead/pmc/.*\.pdb$
+^languages/plumhead/pmc/.*\.pdb/
+^languages/plumhead/pmc/.*\.so$
+^languages/plumhead/pmc/.*\.so/
 # generated from svn:ignore of 'languages/plumhead/src/antlr3/'
 ^languages/plumhead/src/antlr3/.*\.class$
 ^languages/plumhead/src/antlr3/.*\.class/

Modified: trunk/languages/plumhead/config/makefiles/root.in
==============================================================================
--- trunk/languages/plumhead/config/makefiles/root.in	(original)
+++ trunk/languages/plumhead/config/makefiles/root.in	Sat Feb 23 12:43:26 2008
@@ -1,15 +1,30 @@
 # $Id$
 
+# Configuration settings
+LOAD_EXT = @load_ext@
+O        = @o@
+
 # Set up commands
 PARROT        = ../../parrot@exe@
 PERL          = @perl@
 RM_F          = @rm_f@
 RECONFIGURE   = $(PERL) @build_dir@/tools/dev/reconfigure.pl
+PMCBUILD      = $(PERL) @build_dir@/tools/build/dynpmc.pl
 
 # Set up directories
 BUILD_DIR     = @build_dir@
 TGE_DIR       = ../../compilers/tge
 LIBRARY_DIR   = @build_dir@/runtime/parrot/library
+PMC_DIR       = pmc
+PARROT_DYNEXT = @build_dir@/runtime/parrot/dynext
+
+
+# Set up PMCs
+PMCS = \
+  phparray
+
+PMC_FILES = \
+  $(PMC_DIR)/phparray.pmc
 
 
 # default
@@ -91,6 +106,18 @@
 	@echo 'Be sure to have set CLASSPATH as laid out in docs/antlr3.pod'
 	javac src/antlr3/*.java 
 
+pmc : pmc/php_group$(LOAD_EXT)
+
+pmc/php_group$(LOAD_EXT) : $(PMC_FILES)
+	@cd $(PMC_DIR) && $(PMCBUILD) generate $(PMCS)
+	@cd $(PMC_DIR) && $(PMCBUILD) compile $(PMCS)
+	@cd $(PMC_DIR) && $(PMCBUILD) linklibs $(PMCS)
+	@cd $(PMC_DIR) && $(PMCBUILD) copy "--destination=$(PARROT_DYNEXT)" $(PMCS)
+
+
+pmc-test : pmc
+	$(PERL) -I../../lib t/pmc/array.t
+
 src/common/plumheadlib.pbc: src/common/builtins.pir
 	$(PARROT) -o src/common/plumheadlib.pbc src/common/builtins.pir
 
@@ -128,7 +155,7 @@
 test-yacc:
 	- cd .. && $(PERL) -I../lib -I plumhead/lib plumhead/t/harness --with-yacc
 
-clean: clean-common clean-pct clean-antlr3 clean-test
+clean: clean-common clean-pct clean-antlr3 clean-test clean-pmc
 
 clean-common:
 	$(RM_F) src/common/plumheadlib.pbc plumhead.pbc
@@ -142,5 +169,8 @@
 clean-test:
 	$(RM_F) t/*.php t/*.pir t/*.out 
 
+clean-pmc:
+	$(RM_F) pmc/*.c pmc/*.h pmc/*.o pmc/*.so pmc/*.dump
+
 realclean: clean
 	$(RM_F) Makefile

Added: trunk/languages/plumhead/pmc/phparray.pmc
==============================================================================
--- (empty file)
+++ trunk/languages/plumhead/pmc/phparray.pmc	Sat Feb 23 12:43:26 2008
@@ -0,0 +1,1991 @@
+/*
+Copyright (C) 2005-2008, The Perl Foundation.
+$Id$
+
+=head1 NAME
+
+pmc/phparray.pmc - PHP array
+
+=head1 DESCRIPTION
+
+C<PHPAray> provides an implementation of PHP arrays.  These so-called arrays
+are actually hashes which use integer or string keys.  Stored vaues may
+arbitrarily types.  The order of insertion is preserved and can be arbitrarily
+reordered independently of keys and values.
+
+=head2 Methods
+
+=over 4
+
+=cut
+
+*/
+
+#include "parrot/parrot.h"
+
+#define HASH_SEED 12345
+#define PMC_type(pmc)      ((pmc)->vtable->base_type)
+
+#if 0
+#  define dprintf(...) printf(__VA_ARGS__)
+#else
+#  define dprintf(...)
+#endif
+
+#define PREPEND_TO_BUCKET_LIST(b, list) \
+    if ((list) == NULL) {                 \
+        (list) = (b);                       \
+        (b)->bucketNext = NULL;           \
+        (b)->bucketPrev = NULL;           \
+    }                                   \
+    else {                              \
+        (list)->bucketPrev = (b);           \
+        (b)->bucketNext = (list);           \
+        (b)->bucketPrev = NULL;           \
+        (list) = (b);                       \
+    }
+
+
+#define PREPEND_TO_TABLE_LIST(b, list)  \
+    if ((list)->tableHead == NULL) {      \
+        (list)->internalPointer = (b);      \
+        (list)->tableHead = (b);            \
+        (list)->tableTail = (b);            \
+    }                                   \
+    else {                              \
+        (list)->tableHead->tablePrev = (b); \
+        (b)->tableNext = (list)->tableHead; \
+        (b)->tablePrev = NULL;            \
+        (list)->tableHead = (b);            \
+    }
+
+#define APPEND_TO_TABLE_LIST(b, list)   \
+    if ((list)->tableHead == NULL) {      \
+        (list)->internalPointer = (b);      \
+        (list)->tableHead = (b);            \
+        (list)->tableTail = (b);            \
+    }                                   \
+    else {                              \
+        (list)->tableTail->tableNext = (b); \
+        (b)->tablePrev = (list)->tableTail; \
+        (b)->tableNext = NULL;            \
+        (list)->tableTail = (b);            \
+    }
+
+
+/* XXX: temporary workaround until VTABLE_is_equal starts working in the
+ * context this code needs: see RT #50878 */
+#define VTABLE_is_equal(a, b, c) mmd_dispatch_i_pp((a), (b), (c), MMD_EQ)
+
+typedef enum {
+    APPEND,
+    PREPEND
+} add_type;
+
+
+typedef struct bucket {
+    struct bucket *tableNext;
+    struct bucket *tablePrev;
+    struct bucket *bucketNext;
+    struct bucket *bucketPrev;
+    PMC *key;
+    PMC *value;
+    INTVAL hash;
+} Bucket;
+
+typedef struct hashtable {
+    Bucket *internalPointer;
+    Bucket *tableHead;
+    Bucket *tableTail;
+    Bucket **buckets;
+    INTVAL elementCount;
+    INTVAL capacity;
+    INTVAL hashMask;
+    INTVAL nextIndex;
+} HashTable;
+
+void array_key_convert(PARROT_INTERP, PMC **key);
+void add_to_hashtable(PARROT_INTERP, HashTable*, PMC*, PMC*, add_type);
+PMC* get_from_hashtable(PARROT_INTERP, HashTable*, PMC*);
+PMC* delete_from_hashtable(PARROT_INTERP, HashTable *, PMC*);
+INTVAL find_in_hashtable(PARROT_INTERP, HashTable*, PMC*);
+INTVAL phparray_hash(PARROT_INTERP, PMC*);
+void hash_check(PARROT_INTERP, HashTable*);
+void renumber_hash(PARROT_INTERP, HashTable*);
+void resize_and_rehash(PARROT_INTERP, HashTable*);
+
+INTVAL phparray_hash(PARROT_INTERP, PMC *key) {
+    if (PMC_type(key) == enum_class_Integer)
+        return VTABLE_get_integer(interp, key);
+    else if (PMC_type(key) == enum_class_String) {
+        STRING *key_str = VTABLE_get_string(interp, key);
+        return string_hash(interp, key_str, HASH_SEED);
+    }
+    else
+        real_exception(interp, NULL, INVALID_OPERATION,
+                "must use integer or string keys in phparray_hash");
+}
+
+/*If the key is a String PMC and can be converted an integer according to PHP's rules, do so*/
+void array_key_convert(PARROT_INTERP, PMC **key) {
+
+    STRING *key_str, *c0, *c1;
+    PMC *index_pmc;
+
+    /*try to convert the string to an int*/
+    if (PMC_type(*key) == enum_class_String) {
+
+        index_pmc = pmc_new(interp, enum_class_Integer);
+        VTABLE_set_integer_native(interp, index_pmc, (INTVAL)0);
+
+        /*if there's only one char and it's a digit*/
+        if (VTABLE_elements(interp, *key) == 1) {
+            c0 = VTABLE_get_string_keyed(interp, *key, index_pmc);
+
+            /*I should be able to get away with this when I'm just checking the first char*/
+            if (isdigit(*c0->strstart)) {
+                INTVAL key_int = VTABLE_get_integer(interp, *key);
+                *key = pmc_new(interp, enum_class_Integer);
+                VTABLE_set_integer_native(interp, *key, key_int);
+                dprintf("converting string key %d to int key\n",(int)key_int);
+            }
+        } 
+        else {
+            INTVAL key_int = VTABLE_get_integer(interp, *key);
+            c0 = VTABLE_get_string_keyed(interp, *key, index_pmc);
+            VTABLE_increment(interp, index_pmc);
+            c1 = VTABLE_get_string_keyed(interp, *key, index_pmc);
+            if (key_int != 0 && *c0->strstart != '0' && *c0->strstart != '-') {
+                *key = pmc_new(interp, enum_class_Integer);
+                VTABLE_set_integer_native(interp, *key, key_int);
+                dprintf("converting string key %d to int key\n",(int)key_int);
+            }
+            else if (*c0->strstart == '-' && *c1->strstart != '0') {
+                *key = pmc_new(interp, enum_class_Integer);
+                VTABLE_set_integer_native(interp, *key, key_int);
+                dprintf("converting string key %d to int key\n",(int)key_int);
+            }
+        }
+    }
+}
+
+void add_to_hashtable(PARROT_INTERP, HashTable *ht, PMC *key, PMC *value, add_type type) {
+
+    uint index;
+    Bucket *newB, *b;
+    INTVAL curr_index, hash;
+    char *key_cstr, *value_cstr;
+
+    array_key_convert(interp, &key);
+
+    hash = phparray_hash(interp, key);
+    index = ht->hashMask & hash;
+    b = ht->buckets[index];
+
+    if (PMC_type(key) == enum_class_Integer && VTABLE_get_integer(interp, key) >= ht->nextIndex) {
+        
+        curr_index = VTABLE_get_integer(interp, key);
+        if (curr_index < 0)
+            ht->nextIndex = 0;
+        else
+            ht->nextIndex = ++curr_index;
+        dprintf("nextIndex changed to %d\n", (int)ht->nextIndex);
+    }
+    else if (PMC_type(key) == enum_class_Integer)
+        dprintf("nextIndex is %d, inserted key is %d\n", (int)ht->nextIndex,
+                (int)VTABLE_get_integer(interp, key));
+    else {
+        dprintf("nextIndex doesn't care because key is a string\n");
+    }
+
+    dprintf("storing item with hash %X of type %d in bucket #%d of hashtable at 0x%X\n",
+            (uint)hash, (uint)PMC_type(value), index, (uint)ht);
+    key_cstr   = string_to_cstring(interp, VTABLE_get_string(interp, key));
+    value_cstr = string_to_cstring(interp, VTABLE_get_string(interp, value));
+    dprintf("pair maps \"%s\" => \"%s\"\n", key_cstr, value_cstr);
+    string_cstring_free(key_cstr);
+    string_cstring_free(value_cstr);
+
+    while (b != NULL) {
+        if (b->hash == hash && VTABLE_is_equal(interp, key, b->key)) {
+            /*XXX: you may have to update some other pointers too
+              XXX: there may be some DOD marking of some kind
+              */
+            b->value = value;
+            return;
+        }
+        b = b->bucketNext;
+    }
+    dprintf("key hasn't been used yet; making new bucket\n");
+
+    newB = (Bucket*) mem_allocate_zeroed_typed(Bucket);
+    newB->key = key;
+    newB->value = value;
+    newB->hash = hash;
+    PREPEND_TO_BUCKET_LIST(newB, ht->buckets[index]);
+
+    if (type == APPEND) {
+        APPEND_TO_TABLE_LIST(newB, ht);
+    } else if (type == PREPEND) {
+        PREPEND_TO_TABLE_LIST(newB, ht);
+    }
+    ht->elementCount++;
+
+    hash_check(interp, ht);
+    if (ht->elementCount <= ht->capacity)
+        return;
+    resize_and_rehash(interp, ht);
+}
+
+PMC* delete_from_hashtable(PARROT_INTERP, HashTable *ht, PMC *key) {
+
+    INTVAL hash = phparray_hash(interp, key);
+    INTVAL index;
+    Bucket *b;
+    PMC *pmc;
+
+    array_key_convert(interp, &key);
+
+    index = ht->hashMask & hash;
+    b = ht->buckets[index];
+
+    while (b != NULL) {
+        if (b->hash == hash && VTABLE_is_equal(interp, key, b->key)) {
+            /*XXX: you may have to update some other pointers too
+              XXX: there may be some DOD marking of some kind
+              */
+        /*do some stupid pointer juggling to delete the element*/
+
+            pmc = b->value;
+            return pmc;
+        }
+        b = b->bucketNext;
+        ht->elementCount--;
+    }
+    dprintf("the thing doesn't seem to be in the hash\n");
+
+    hash_check(interp, ht);
+    return PMCNULL;
+}
+
+PMC* get_from_hashtable(PARROT_INTERP, HashTable *ht, PMC *key) {
+    INTVAL index, hash, i;
+    Bucket *b;
+
+    array_key_convert(interp, &key);
+
+    hash = phparray_hash(interp, key);
+    index = ht->hashMask & hash;
+    b = ht->buckets[index];
+
+    i = 0;
+    dprintf("getting thing with hash %X in hashtable\n", (uint)hash);
+    while (b != NULL) {
+        dprintf("searching bucket #%d with key at 0X%X and b->key at 0X%X\n",
+        (int)i, (uint)key, (uint)b->key);
+        i++;
+        if (b->hash == hash && VTABLE_is_equal(interp, key, b->key)) {
+            return b->value;
+        }
+        b = b->bucketNext;
+    }
+    dprintf("thing not found\n");
+    return PMCNULL;
+}
+
+INTVAL find_in_hashtable(PARROT_INTERP, HashTable *ht, PMC *key) {
+    INTVAL hash = phparray_hash(interp, key);
+    INTVAL index, i;
+    Bucket *b;
+
+    index = ht->hashMask & hash;
+    b = ht->buckets[index];
+
+    i = 0;
+    dprintf("looking for thing with hash %X in hashtable\n", (uint)hash);
+    while (b != NULL) {
+        dprintf("searching bucket #%d with key at %X and b->key at %X\n",
+                (int)i, (uint)key, (uint)b->key);
+        i++;
+        if (b->hash == hash && VTABLE_is_equal(interp, key, b->key)) {
+            return 1;
+        }
+        b = b->bucketNext;
+    }
+    return 0;
+}
+
+void hash_check(PARROT_INTERP, HashTable *ht) {
+
+    INTVAL i, bucket_order_count, insert_order_count;
+    int key_type, value_type;
+    char *key_str, *value_str;
+    Bucket *b;
+
+    dprintf("checking hash at %X\n", (uint)ht);
+    dprintf("capacity = %d, mask = %d, elementCount = %d, nextIndex = %d\n",
+            (int)ht->capacity, (int)ht->hashMask, (int)ht->elementCount, (int)ht->nextIndex);
+    bucket_order_count = 0;
+    for (i = 0; i < ht->capacity; i++) {
+        Bucket *b = ht->buckets[i];
+        dprintf("checking bucket #%d...", (int)i);
+        while (b != NULL) {
+            key_type   = PMC_type(b->key);
+            value_type = PMC_type(b->value);
+            key_str   = string_to_cstring(interp, VTABLE_get_string(interp, b->key));
+            value_str = string_to_cstring(interp, VTABLE_get_string(interp, b->value));
+            dprintf("\n  bucket at 0x%X maps \"%s\"(%d) => \"%s\"(%d)",
+                    (uint)b, key_str, (uint)key_type, value_str, (uint)value_type);
+            string_cstring_free(key_str);
+            string_cstring_free(value_str);
+            b = b->bucketNext;
+            bucket_order_count++;
+        }
+        dprintf("\n");
+    }
+    dprintf("now checking by insertion order\n");
+    b = ht->tableHead;
+    insert_order_count = 0;
+    while (b != NULL) {
+        key_type   = PMC_type(b->key);
+        value_type = PMC_type(b->value);
+        key_str   = string_to_cstring(interp, VTABLE_get_string(interp, b->key));
+        value_str = string_to_cstring(interp, VTABLE_get_string(interp, b->value));
+        dprintf("  bucket at 0x%X maps \"%s\"(%d) => \"%s\"(%d)\n",
+                (uint)b, key_str, (uint)key_type, value_str, (uint)value_type);
+        string_cstring_free(key_str);
+        string_cstring_free(value_str);
+        b = b->tableNext;
+        insert_order_count++;
+    }
+    dprintf("%d buckets expected, %d found by bucket order, %d found by insert order\n",
+            (int)ht->elementCount, (int)bucket_order_count, (int)insert_order_count);
+}
+
+void renumber_hash(PARROT_INTERP, HashTable *ht) {
+    Bucket *b;
+    INTVAL index;
+
+    b = ht->tableHead;
+    dprintf("renumbering hash at %X\n", (uint)ht);
+    index = 0;
+    while (b != NULL) {
+        if (PMC_type(b->key) == enum_class_Integer) {
+            VTABLE_set_integer_native(interp, b->key, index);
+            index++;
+        }
+        b = b->bucketNext;
+    }
+    ht->nextIndex = ++index;
+}
+
+void resize_and_rehash(PARROT_INTERP, HashTable *ht) {
+    Bucket **buckets;
+    Bucket *b;
+    INTVAL index;
+
+    hash_check(interp, ht);
+
+
+    /* resize*/
+    mem_sys_free(ht->buckets);
+    ht->capacity <<= 1;
+    ht->hashMask = ht->capacity - 1;
+    ht->buckets = (Bucket**)mem_allocate_n_zeroed_typed(ht->capacity, Bucket);
+
+    /* rehash*/
+    b = ht->tableHead;
+    while (b != NULL) {
+        index = b->hash & ht->hashMask;
+        PREPEND_TO_BUCKET_LIST(b, ht->buckets[index]);
+        b = b->tableNext;
+    };
+
+    hash_check(interp, ht);
+}
+
+pmclass PHPArray
+    provides hash
+    provides array
+    need_ext
+    dynpmc
+    group php_group
+    hll PHP {
+
+/*
+
+=item C<PMC* add(PMC*, PMC*)>
+
+=item C<PMC* i_add(PMC*, PMC*)>
+
+Insert all key/value pairs from the second array into the first
+
+=cut
+
+*/
+
+
+    /*array + array -> combine elements */
+    PMC* add(PMC *value, PMC *dest) {
+        return PMCNULL;
+    }
+
+    void i_add(PMC *value) {
+    }
+
+/*
+
+=item C<PMC* is_equal(PMC*)>
+
+Determine equality between this and another PMC.  Two PHPArrays PMCs are equal
+if they contain the same key/value pairs, regardless of order.  This is the
+same behavoir that is found in PHP.
+
+=cut
+
+*/
+    INTVAL is_equal(PMC *value) {
+        /*XXX: figure out what it's appropriate to compare this PMC to.
+          ATM I'm thinking soemthing like
+          VTABLE_does(hash) && VTABLE_does(array) && all key/value pairs match
+          */
+        return (INTVAL)0;
+    }
+
+/*
+
+=item C<PMC* is_equal_num(PMC*)>
+
+=item C<PMC* is_equal_string(PMC*)>
+
+Determine equality between this PHPArray PMC and a string or number.  According
+to PHP an array is never equal to a string or number.  If only everything were
+that easy.
+
+=cut
+
+*/
+    INTVAL is_equal_num(PMC *value) {
+        return (INTVAL)0;
+    }
+
+    INTVAL is_equal_string(PMC *value) {
+        return (INTVAL)0;
+    }
+
+    /* not sure if this needs to be implemented
+    INTVAL cmp (PMC *value) {
+        return (INTVAL)0;
+    }
+
+    INTVAL cmp_num (PMC *value) {
+        return (INTVAL)0;
+    }
+
+    INTVAL cmp_string (PMC *value) {
+        return (INTVAL)0;
+    }*/
+
+/*
+
+=item C<void assign_pmc(PMC*)>
+
+If the passed-in PMC is array-like and/or hash-like, copy all key/value pairs
+into this PMC.  If the PMC is a PHPArray, make a clone.
+
+=cut
+
+*/
+
+    void assign_pmc(PMC *value) {
+        Bucket *b1, *b2;
+        INTVAL new_size;
+        PMC *new_key, *new_value;
+        HashTable *orig_ht, *new_ht;
+
+        orig_ht = (HashTable*) PMC_struct_val(SELF);
+        new_size = orig_ht->elementCount;
+
+        if (PMC_type(value) == PMC_type(SELF)) {
+            new_ht = (HashTable*) PMC_struct_val(value);
+            /*XXX: do I need to delete the old value?*/
+            /*delete everything in value*/
+            /*free all buckets*/
+            b1 = new_ht->tableHead;
+            while (b1 != NULL) {
+                b2 = b1;
+                b1 = b1->tableNext;
+                mem_sys_free(b2);
+            }
+            if (new_size > new_ht->elementCount) {
+                /*resize it to this PMC*/
+                mem_sys_free(new_ht->buckets);
+                new_ht->buckets = (Bucket**)
+                    mem_allocate_n_zeroed_typed(new_size, Bucket);
+                new_ht->elementCount = new_size;
+                new_ht->hashMask = orig_ht->hashMask;
+            }
+
+            b1 = orig_ht->tableHead;
+            /* XXX: it'd probably be better to implement COW here*/
+            while (b1 != NULL) {
+                /*XXX: implement: insert copies of key/value pairs*/
+
+            }
+
+        }
+        else {
+            /*do the Right Thing according to whether the passed-in PMC
+             * implements a hash-like and/or array-like interface*/
+        }
+    }
+
+/*
+
+=item C<PMC* clone()>
+
+Return a clone of this PHPArray.
+
+=cut
+
+*/
+
+    PMC* clone() {
+        return PMCNULL;
+    }
+
+    /* nothing else implements this, so I won't either
+    PMC* clone_pmc (PMC *args) {
+        return PMCNULL;
+    }*/
+
+/*
+
+=item C<void delete_keyed(PMC*)>
+
+=item C<void delete_keyed_int(INTVAL)>
+
+=item C<void delete_keyed_str(STRING*)>
+
+Remove the element at key.
+
+=cut
+
+*/
+
+    void delete_keyed(PMC *key) {
+        /*XXX: implement keyed code properly*/
+        delete_from_hashtable(INTERP, (HashTable*)PMC_struct_val(SELF), key);
+    }
+
+    void delete_keyed_int(INTVAL key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        SELF.delete_keyed(key_pmc);
+    }
+
+    void delete_keyed_str(STRING *key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        SELF.delete_keyed(key_pmc);
+    }
+
+/*
+
+=item C<void destroy()>
+
+Free the memory associated with this PHPArray's underlying structs.
+
+=cut
+
+*/
+
+    void destroy() {
+        HashTable *ht = (HashTable*)PMC_struct_val(SELF);
+        Bucket *b1, *b2;
+
+        b1 = ht->tableHead;
+        while (b1 != NULL) {
+            b2 = b1;
+            b1 = b1->tableNext;
+            mem_sys_free(b2);
+        }
+        mem_sys_free(ht->buckets);
+        mem_sys_free(ht);
+    }
+
+/*
+
+=item C<INTVAL elements()>
+
+Returns the number of elements in this PHPArray.
+
+=cut
+
+*/
+    INTVAL elements() {
+        HashTable *ht = (HashTable*)PMC_struct_val(SELF);
+        return (INTVAL) ht->elementCount;
+    }
+
+/*
+
+=item C<INTVAL exists_keyed(PMC*)>
+
+=item C<INTVAL exists_keyed_int(INTVAL)>
+
+=item C<INTVAL exists_keyed_string(STRING)>
+
+Returns TRUE if the element at C<key> exists; otherwise returns false.
+
+=cut
+
+*/
+    INTVAL exists_keyed(PMC *key) {
+        /*XXX: implement keyed code properly*/
+        return find_in_hashtable(INTERP, (HashTable*)PMC_struct_val(SELF), key);
+    }
+
+    INTVAL exists_keyed_int(INTVAL key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        return SELF.exists_keyed(key_pmc);
+    }
+
+    INTVAL exists_keyed_str(STRING *key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        return SELF.exists_keyed(key_pmc);
+    }
+
+    /* XXX: probably need to implement
+    void freeze (visit_info *info) {
+    }*/
+
+/*
+
+=item C<INTVAL get_bool()>
+
+Return TRUE if this PHPArray has one or more elements, return FALSE otherwise.
+
+=cut
+
+*/
+    INTVAL get_bool() {
+        return SELF.elements() == 1;
+    }
+/*
+
+=item C<INTVAL get_integer_keyed(PMC*)>
+
+=item C<INTVAL get_integer_keyed_int(INTVAL)>
+
+=item C<INTVAL get_integer_keyed_str(STRING*)>
+
+Return the integer value of the elements a C<key>.
+
+=cut
+
+*/
+
+    INTVAL get_integer_keyed(PMC *key) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s;
+        char key_is_int;
+
+        STRING *key_str = key_set_to_string(INTERP, key);
+        char *key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("get_integer_keyed called with key = %s\n", key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return 0;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("get_integer_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from get_integer_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            return SELF.get_integer_keyed_int(index_i);
+        }
+        else if (next_key == NULL && !key_is_int) {
+            return SELF.get_integer_keyed_str(index_s);
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == NULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == NULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_integer_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_get_integer_keyed(INTERP, box, next_key);
+
+    }
+
+    INTVAL get_integer_keyed_int(INTVAL key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return 0;
+
+        return VTABLE_get_integer(INTERP, value);
+    }
+
+    INTVAL get_integer_keyed_str(STRING *key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return 0;
+
+        return VTABLE_get_integer(INTERP, value);
+    }
+
+/*
+
+=item C<PMC* get_iter()>
+
+Return a new iterator for this PHPArray.
+
+=cut
+
+*/
+    PMC* get_iter() {
+        /*XXX: implement */
+        return PMCNULL;
+    }
+
+/*
+
+=item C<FLOATVAL get_number_keyed(PMC*)>
+
+=item C<FLOATVAL get_number_keyed_int(INTVAL)>
+
+=item C<FLOATVAL get_number_keyed_str(STRING*)>
+
+Return the float value of the element at C<key>.
+
+=cut
+
+*/
+
+    FLOATVAL get_number_keyed(PMC *key) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s;
+        char key_is_int;
+
+        STRING *key_str = key_set_to_string(INTERP, key);
+        char *key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("get_number_keyed called with key = %s\n", key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return 0;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("get_number_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from get_number_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            return SELF.get_number_keyed_int(index_i);
+        }
+        else if (next_key == NULL && !key_is_int) {
+            return SELF.get_number_keyed_str(index_s);
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == NULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == NULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_number_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_get_number_keyed(INTERP, box, next_key);
+
+    }
+
+    FLOATVAL get_number_keyed_int(INTVAL key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return 0.0;
+
+        return VTABLE_get_number(INTERP, value);
+    }
+
+    FLOATVAL get_number_keyed_str(STRING *key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return 0.0;
+
+        return VTABLE_get_number(INTERP, value);
+    }
+
+/*
+
+=item C<PMC get_pmc_keyed(PMC*)>
+
+=item C<PMC get_pmc_keyed_int(INTVAL)>
+
+=item C<PMC get_pmc_keyed_str(STRING*)>
+
+Return the string value of the element at C<key>.
+
+=cut
+
+*/
+
+    PMC* get_pmc_keyed(PMC *key) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s;
+        char key_is_int;
+
+        STRING *key_str = key_set_to_string(INTERP, key);
+        char *key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("get_pmc_keyed called with key = %s\n", key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return 0;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("get_pmc_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from get_pmc_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the aggregate PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            return SELF.get_pmc_keyed_int(index_i);
+        }
+        else if (next_key == NULL && !key_is_int) {
+            return SELF.get_pmc_keyed_str(index_s);
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == NULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == NULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_pmc_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_get_pmc_keyed(INTERP, box, next_key);
+    }
+
+    PMC *get_pmc_keyed_int(INTVAL key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        hash_check(INTERP, (HashTable*) PMC_struct_val(SELF));
+        return get_from_hashtable(INTERP, (HashTable*)PMC_struct_val(SELF), key_pmc);
+    }
+
+    PMC* get_pmc_keyed_str(STRING *key) {
+        PMC *key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        hash_check(INTERP, (HashTable*) PMC_struct_val(SELF));
+        return get_from_hashtable(INTERP, (HashTable*)PMC_struct_val(SELF), key_pmc);
+    }
+
+/*
+
+=item C<STRING* get_string()>
+
+Return the string representation of this array.  This is simply the string C<Array>.
+
+=cut
+
+*/
+    STRING* get_string() {
+        return const_string(INTERP, "Array");
+    }
+
+/*
+
+=item C<STRING* get_string_keyed(PMC*)>
+
+=item C<STRING* get_string_keyed_int(INT)>
+
+=item C<STRING* get_string_keyed_str(STRING*)>
+
+Return the string value of the element at C<key>.
+
+=cut
+
+*/
+    STRING* get_string_keyed(PMC *key) {
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s;
+        char key_is_int;
+
+        STRING *key_str = key_set_to_string(INTERP, key);
+        char *key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("get_string_keyed called with key = %s\n", key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return 0;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("get_string_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from get_string_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            return SELF.get_string_keyed_int(index_i);
+        }
+        else if (next_key == NULL && !key_is_int) {
+            return SELF.get_string_keyed_str(index_s);
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == NULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == NULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from get_string_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_get_string_keyed(INTERP, box, next_key);
+
+    }
+
+    STRING* get_string_keyed_int(INTVAL key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return const_string(INTERP, "");
+
+        return VTABLE_get_string(INTERP, value);
+    }
+
+    STRING* get_string_keyed_str(STRING *key) {
+        PMC *key_pmc, *value;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        value = get_from_hashtable(INTERP, ht, key_pmc);
+
+        if (value == PMCNULL)
+            return const_string(INTERP, "");
+
+        return VTABLE_get_string(INTERP, value);
+    }
+
+/*
+
+=item C<void init()>
+
+=item C<void init_pmc(PMC*)>
+
+Initialize this PHPArray's internal structures
+
+=cut
+
+*/
+    void init() {
+        HashTable *ht;
+
+        PObj_custom_mark_destroy_SETALL(SELF);
+
+        ht = mem_allocate_zeroed_typed(HashTable);
+        /*initialize the hash to contain 4 buckets*/
+        ht->capacity = 4;
+        ht->hashMask = ht->capacity - 1;
+        ht->buckets = (Bucket**)mem_allocate_n_zeroed_typed(ht->capacity, Bucket);
+        PMC_struct_val(SELF) = ht;
+
+    }
+
+    void init_pmc (PMC *initializer) {
+        if (initializer == PMCNULL)
+            DYNSELF.init();
+    }
+
+    /* not sure if I care about these
+    PMC* inspect () {
+        return PMCNULL;
+    }
+
+    PMC* inspect_str (STRING *what) {
+        return PMCNULL;
+    }
+
+    PMC* instantiate (PMC *sig) {
+        return PMCNULL;
+    }
+
+    opcode_t* invoke (void *next) {
+        return (opcode_t*)0;
+    }*/
+
+
+/*
+
+=item C<INTVAL is_same(PMC*)>
+
+Return TRUE if this PHPArray and the passed-in PMC refer to the same region in memory.
+
+=cut
+
+*/
+    INTVAL is_same(PMC *other) {
+        return PMC_struct_val(other) == PMC_struct_val(SELF) &&
+               other->vtable == SELF->vtable;
+    }
+
+/*
+
+=item C<void mark()>
+
+Mark the PHPArray and all contents as live.
+
+=cut
+
+*/
+    void mark() {
+        Bucket *b;
+        HashTable *ht;
+        INTVAL elementCount;
+        int i;
+
+        ht = (HashTable*) PMC_struct_val(SELF);
+        dprintf("marking hash at %X\n", (uint)ht);
+        elementCount = ht->elementCount;
+        i = 0;
+        for (i = 0; i < elementCount; i++) {
+            b = ht->buckets[i];
+            dprintf("marking bucket #%d\n", i);
+            while (b != NULL) {
+              /*XXX: I don't think I need to call pobject lives on a bucket
+                pobject_lives(INTERP, (PObj*)b);*/
+                pobject_lives(INTERP, (PObj*)b->key);
+                pobject_lives(INTERP, (PObj*)b->value);
+                b = b->bucketNext;
+            }
+            i++;
+        }
+    }
+
+    /*XXX: I'm pretty sure I don't need to implement these, but
+      I'm leaving them here until I understand the Iterator PMC
+      well enough to do otherwise.
+
+    PMC* nextkey_keyed (PMC *key, INTVAL what) {
+        return PMCNULL;
+    }
+
+    PMC* nextkey_keyed_int (INTVAL key, INTVAL what) {
+        return PMCNULL;
+    }
+
+    PMC* nextkey_keyed_str (STRING *key, INTVAL what) {
+        return PMCNULL;
+    }*/
+
+/*
+
+=item C<FLOATVAL pop_float()>
+
+=item C<INTVAL pop_integer()>
+
+=item C<PMC* pop_pmc()>
+
+=item C<STRING* pop_string()>
+
+Remove and return the last element in the list according to internal ordering.
+After removing the element, the internal pointer is reset to the first element.
+
+=cut
+
+*/
+    FLOATVAL pop_float() {
+        PMC *p = SELF.pop_pmc();
+        return VTABLE_get_number(INTERP, p);
+    }
+
+    INTVAL pop_integer() {
+        PMC *p = SELF.pop_pmc();
+        return VTABLE_get_integer(INTERP, p);
+    }
+
+    PMC* pop_pmc() {
+        Bucket *new_tail;
+        struct bucket *tail;
+        HashTable *ht;
+        PMC *popped;
+
+        if (ht->tableHead == NULL) {
+            return PMCNULL;
+        }
+
+        ht = (HashTable*) PMC_struct_val(SELF);
+        if (ht->tableHead == ht->tableTail) {
+            popped = ht->tableHead->value;
+            mem_sys_free(ht->tableHead);
+            ht->internalPointer = NULL;
+            ht->tableHead = NULL;
+            ht->tableTail = NULL;
+            ht->elementCount = 0;
+            ht->nextIndex = 0;
+        }
+        else {
+            tail = ht->tableTail;
+            new_tail = ht->tableTail->tablePrev;
+            new_tail->tableNext = NULL;
+            ht->tableTail = new_tail;
+            ht->internalPointer = ht->tableHead;
+            popped = tail->value;
+            mem_sys_free(tail);
+        }
+
+        return popped;
+    }
+
+    STRING* pop_string() {
+        PMC *p = SELF.pop_pmc();
+        return VTABLE_get_string(INTERP, p);
+    }
+
+/*
+
+=item C<void push_float(FLOATVAL)>
+
+=item C<void push_integer(INTVAL)>
+
+=item C<void push_pmc(PMC*)>
+
+=item C<void push_string(STRING*)>
+
+Add C<value> to the end of the PHPArray according to internal ordering.  This
+does B<not> reset the internal pointer.
+
+=cut
+
+*/
+    void push_float(FLOATVAL value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_Float);
+        VTABLE_set_number_native(INTERP, value_pmc, value);
+        SELF.push_pmc(value_pmc);
+    }
+
+    void push_integer(INTVAL value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, value_pmc, value);
+        SELF.push_pmc(value_pmc);
+    }
+
+    void push_pmc(PMC *value) {
+        HashTable *ht = (HashTable*)PMC_struct_val(SELF);
+        INTVAL key = ht->nextIndex;
+        ht->nextIndex++;
+        VTABLE_set_pmc_keyed_int(INTERP, SELF, key, value);
+    }
+
+    void push_string(STRING *value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, value_pmc, value);
+        SELF.push_pmc(value_pmc);
+    }
+/*
+
+=item C<void set_string_keyed_str (STRING*, STRING*)>
+
+=item C<void set_string_keyed_int (INTVAL, STRING*)>
+
+=item C<void set_string_keyed (PMC*, STRING*)>
+
+=item C<void set_pmc_keyed_str (STRING*, PMC*)>
+
+=item C<void set_pmc_keyed_int (INTVAL, PMC*)>
+
+=item C<void set_number_keyed_str (STRING*, FLOATVAL)>
+
+=item C<void set_number_keyed_int (INTVAL, FLOATVAL)>
+
+=item C<void set_integer_keyed (PMC*, INTVAL)>
+
+=item C<void set_integer_keyed_str (STRING*, INTVAL)>
+
+=item C<void set_integer_keyed_int (INTVAL, INTVAL)>
+
+Associate C<key> with C<value>.
+
+=cut
+
+*/
+
+    void set_integer_keyed_int(INTVAL key, INTVAL value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_integer_keyed_str(STRING *key, INTVAL value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_integer_keyed(PMC *key, INTVAL value) {
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s, *key_str;
+        char *key_cstr;
+        char key_is_int;
+
+        key_str = key_set_to_string(INTERP, key);
+        key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("set_integer_keyed called with value = %d and key = %s\n", (int)value, key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("set_integer_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from set_integer_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            SELF.set_integer_keyed_int(index_i, value);
+            return;
+        }
+        else if (next_key == NULL && !key_is_int) {
+            SELF.set_integer_keyed_str(index_s, value);
+            return;
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_integer_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_set_integer_keyed(INTERP, box, next_key, value);
+    }
+
+    void set_number_keyed_int(INTVAL key, FLOATVAL value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_Float);
+        VTABLE_set_number_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_number_keyed_str(STRING *key, FLOATVAL value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_Float);
+        VTABLE_set_number_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_number_keyed(PMC *key, FLOATVAL value) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s, *key_str;
+        char *key_cstr;
+        char key_is_int;
+
+        key_str = key_set_to_string(INTERP, key);
+        key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("set_number_keyed called with value = %d and key = %s\n", (int)value, key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("set_number_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from set_number_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            SELF.set_number_keyed_int(index_i, value);
+            return;
+        }
+        else if (next_key == NULL && !key_is_int) {
+            SELF.set_number_keyed_str(index_s, value);
+            return;
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_number_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_set_number_keyed(INTERP, box, next_key, value);
+    }
+
+    void set_pmc_keyed_int(INTVAL key, PMC *value) {
+        PMC *key_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value, APPEND);
+    }
+
+    void set_pmc_keyed_str(STRING *key, PMC *value) {
+        PMC *key_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value, APPEND);
+    }
+
+    void set_pmc_keyed(PMC *key, PMC *value) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s, *key_str;
+        char *key_cstr;
+        char key_is_int;
+
+        key_str = key_set_to_string(INTERP, key);
+        key_cstr = string_to_cstring(INTERP, key_str);
+        dprintf("set_pmc_keyed called with value = %d and key = %s\n", (int)value, key_cstr);
+        string_cstring_free(key_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("set_pmc_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from set_pmc_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the aggregate PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            SELF.set_pmc_keyed_int(index_i, value);
+            return;
+        }
+        else if (next_key == NULL && !key_is_int) {
+            SELF.set_pmc_keyed_str(index_s, value);
+            return;
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_pmc_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_set_pmc_keyed(INTERP, box, next_key, value);
+
+    }
+
+    /* I'm pretty sure I don't care about these
+    void set_pointer_keyed_int(INTVAL key, void *value) {
+    }
+
+    void set_pointer_keyed_str(STRING *key, void *value) {
+    }*/
+
+    void set_string_keyed_int(INTVAL key, STRING *value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_string_keyed_str(STRING *key, STRING *value) {
+        PMC *key_pmc, *value_pmc;
+        HashTable *ht;
+
+        key_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, key_pmc, key);
+        value_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, value_pmc, value);
+        ht = (HashTable*) PMC_struct_val(SELF);
+        add_to_hashtable(INTERP, ht, key_pmc, value_pmc, APPEND);
+    }
+
+    void set_string_keyed(PMC *key, STRING *value) {
+
+        PMC *box, *next_key;
+        INTVAL index_i, key_t, next_key_t;
+        STRING *index_s, *key_str;
+        char *key_cstr, *val_cstr;
+        char key_is_int;
+
+        key_str = key_set_to_string(INTERP, key);
+        key_cstr = string_to_cstring(INTERP, key_str);
+        val_cstr = string_to_cstring(INTERP, value);
+        dprintf("set_string_keyed called with value = '%s' and key = %s\n", val_cstr, key_cstr);
+        string_cstring_free(key_cstr);
+        string_cstring_free(val_cstr);
+
+        /*if key is null*/
+        if (key == NULL) {
+            return;
+        }
+
+        key_t = key_type(INTERP, key);
+
+        /*figure out type of the key*/
+        if (key_t & KEY_integer_FLAG) {
+            index_i = key_integer(INTERP, key);
+            dprintf("set_string_keyed: integer index is %d\n", (int)index_i);
+            key_is_int = 1;
+        }
+        else if (key_t & KEY_string_FLAG) {
+            index_s = key_string(INTERP, key);
+            key_cstr = string_to_cstring(INTERP, index_s);
+            dprintf("set_string_keyed: string index is '%s'\n", key_cstr);
+            string_cstring_free(key_cstr);
+            key_is_int = 0;
+        }
+        else {
+            dprintf("exception from set_string_keyed, current key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        next_key = key_next(INTERP, key);
+
+        /*if this is the PMC being requested...*/
+        if (next_key == NULL && key_is_int) {
+            dprintf("retrieving value from index %d\n", (int)index_i);
+            SELF.set_string_keyed_int(index_i, value);
+            return;
+        }
+        else if (next_key == NULL && !key_is_int) {
+            SELF.set_string_keyed_str(index_s, value);
+            return;
+        }
+
+        next_key_t = key_type(INTERP, next_key);
+
+        if (key_t & KEY_integer_FLAG) {
+            dprintf("box has int key %d\n", (int)index_i);
+            box = SELF.get_pmc_keyed_int(index_i);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at int index %d\n", (int)index_i);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_int(index_i, box);
+            }
+        }
+        else if (key_t & KEY_string_FLAG) {
+            char *cstr = string_to_cstring(INTERP, index_s);
+            dprintf("box has string key %s\n", cstr);
+            box = SELF.get_pmc_keyed_str(index_s);
+            if (box == PMCNULL) {
+                dprintf("autovivifying box at string index %s\n", cstr);
+                box = pmc_new(INTERP, DYNSELF.type());
+                SELF.set_pmc_keyed_str(index_s, box);
+            }
+            string_cstring_free(cstr);
+        }
+        else {
+            dprintf("exception from set_integer_keyed, next key\n");
+            real_exception(INTERP, NULL, E_KeyError, "must use integer or string keys");
+        }
+
+        return VTABLE_set_string_keyed(INTERP, box, next_key, value);
+    }
+/*
+
+=item C<PMC* share_ro()>
+
+Mark the PHPArray as shared and read-only.
+
+=cut
+
+*/
+
+    PMC* share_ro() {
+        return PMCNULL;
+    }
+
+/*
+
+=item C<FLOATVAL shift_float()>
+
+=item C<INTVAL shift_integer()>
+
+=item C<PMC* shift_pmc()>
+
+=item C<STRING* shift_string()>
+
+Return the the first item on the list as the type specified, removing it from
+this PHPArray.  All remaining keys with numerical indicies are renumbered
+according to their internal order in the PHPArray, starting from 0.  After
+shifting, the internal pointer is reset to the first element of the PHPArray.
+
+=cut
+
+*/
+    FLOATVAL shift_float() {
+        PMC *p = SELF.shift_pmc();
+        return VTABLE_get_number(INTERP, p);
+    }
+
+    INTVAL shift_integer() {
+        PMC *p = SELF.shift_pmc();
+        return VTABLE_get_integer(INTERP, p);
+    }
+
+    PMC* shift_pmc() {
+        Bucket *new_head;
+        struct bucket *head;
+        HashTable *ht;
+        PMC *shifted;
+        char *str;
+
+        if (ht->tableTail == NULL) {
+            return PMCNULL;
+        }
+
+        ht = (HashTable*) PMC_struct_val(SELF);
+        if (ht->tableHead == ht->tableTail) {
+            shifted = ht->tableTail->value;
+            mem_sys_free(ht->tableTail);
+            ht->internalPointer = NULL;
+            ht->tableHead = NULL;
+            ht->tableTail = NULL;
+            ht->elementCount = 0;
+            ht->nextIndex = 0;
+        }
+        else {
+            head = ht->tableHead;
+            new_head = ht->tableHead->tableNext;
+            new_head->tablePrev = NULL;
+            ht->tableHead = new_head;
+            ht->internalPointer = ht->tableHead;
+            shifted = head->value;
+            mem_sys_free(head);
+        }
+
+        return shifted;
+    }
+
+    STRING* shift_string() {
+    PMC *p = SELF.shift_pmc();
+        return VTABLE_get_string(INTERP, p);
+    }
+
+/*
+    PMC* slice (PMC *key, INTVAL flag) {
+        return PMCNULL;
+    }
+
+    void splice (PMC *value, INTVAL offset, INTVAL count) {
+    }
+
+    void thaw (visit_info *info) {
+    }
+
+    void thawfinish (visit_info *info) {
+    } */
+
+/*
+
+=item C<FLOATVAL unshift_float()>
+
+=item C<INTVAL unshift_integer()>
+
+=item C<PMC* unshift_pmc()>
+
+=item C<STRING* unshift_string()>
+
+Add the passed value to the beginning of this PHPArray.  The value is given an
+integer key of 0 and is placed first in the PHPArray's internal ordering.  All
+integer keys are renumbered starting from 0, according to their internal order
+in the PHPArray.  After unshifting, the internal pointer is reset to point to
+the newly inserted element.
+
+=cut
+
+*/
+    void unshift_float(FLOATVAL value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_Float);
+        VTABLE_set_number_native(INTERP, value_pmc, value);
+        SELF.unshift_pmc(value_pmc);
+    }
+
+    void unshift_integer(INTVAL value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, value_pmc, value);
+        SELF.unshift_pmc(value_pmc);
+    }
+
+    void unshift_pmc(PMC *value) {
+
+        PMC *key_pmc;
+        HashTable *ht;
+
+        ht = (HashTable*) PMC_struct_val(SELF);
+
+        key_pmc = pmc_new(INTERP, enum_class_Integer);
+        VTABLE_set_integer_native(INTERP, key_pmc, ht->nextIndex);
+
+        add_to_hashtable(INTERP, ht, key_pmc, value, PREPEND);
+        renumber_hash(INTERP, ht);
+
+        ht->internalPointer = ht->tableHead;
+    }
+
+    void unshift_string(STRING *value) {
+        PMC *value_pmc = pmc_new(INTERP, enum_class_String);
+        VTABLE_set_string_native(INTERP, value_pmc, value);
+        SELF.unshift_pmc(value_pmc);
+    }
+/*
+
+=item C<void visit(visit_info*)>
+
+Used by freeze and thaw to visit the contents of the PMC.
+
+=cut
+
+*/
+
+    void visit(visit_info *info) {
+
+    }
+
+}
+
+/*
+ * Local variables:
+ *   c-file-style: "parrot"
+ * End:
+ * vim: expandtab shiftwidth=4:
+ */

Added: trunk/languages/plumhead/t/pmc/array.t
==============================================================================
--- (empty file)
+++ trunk/languages/plumhead/t/pmc/array.t	Sat Feb 23 12:43:26 2008
@@ -0,0 +1,243 @@
+#! perl
+# Copyright (C) 2005-2008, The Perl Foundation.
+# $Id$
+
+=head1 NAME
+
+t/pmc/array.t - PHP array
+
+=head1 SYNOPSIS
+
+    % perl -I../../lib t/pmc/array.t
+
+=head1 DESCRIPTION
+
+Tests C<array> type
+(implemented in F<languages/plumhead/pmc/phparray.pmc>).
+
+=cut
+
+use strict;
+use warnings;
+
+use Parrot::Test tests => 10;
+use Test::More;
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'unkeyed get_string' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc p1
+    p1 = new 'PHPArray'
+    print p1
+    print "\n"
+.end
+CODE
+Array
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'int keyed set/get' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    ar[1] = 2746
+    i1 = ar[1]
+    print i1
+    print "\n"
+.end
+CODE
+2746
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'int to string conversion' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local string s
+
+    ar = new 'PHPArray'
+
+    ar[1] = 'string'
+
+    s = ar['1']
+    print s
+    print "\n"
+.end
+CODE
+string
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'string to int conversion' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local string s
+
+    ar = new 'PHPArray'
+
+    ar['1'] = 'right string'
+
+    ar['01'] = 'wrong string'
+    s = ar[1]
+    print s
+    print "\n"
+.end
+CODE
+right string
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'autovivification' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    ar['this';1;'test';'will';'cause';6] = 'autovivifications'
+    s1 = ar['this';1;'test';'will';'cause';6]
+    print s1
+    print "\n"
+.end
+CODE
+autovivifications
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'string keyed set/get' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    ar['x'] = 2746
+    i1 = ar['x']
+    print i1
+    print "\n"
+.end
+CODE
+2746
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'several sets/gets' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    ar[1] = 6
+    ar[2] = 746
+    ar[3] = 76
+    ar[4] = 27
+    ar[5] = 76
+    ar[6] = 2
+    ar[7] = 246
+    ar[8] = 274
+    ar[9] = 74
+    i1 = ar[6]
+    print i1
+    print "\n"
+.end
+CODE
+2
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'various types' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc a, pmc_str
+    .local int i
+    .local string s
+    .local num n
+
+    a = new 'PHPArray'
+    pmc_str = new 'String'
+    pmc_str = 'This is a PMC string.'
+
+    a[0] = 123
+    a['not_pi'] = 3.142938
+    a['string'] = 'normal string'
+    a[8] = pmc_str
+
+    pmc_str = a[8]
+    print pmc_str
+    print "\n"
+    n = a['not_pi']
+    print n
+    print "\n"
+    i = a[0]
+    print i
+    print "\n"
+    s = a['string']
+    print s
+    print "\n"
+
+.end
+CODE
+This is a PMC string.
+3.142938
+123
+normal string
+OUTPUT
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'push/pop' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    push ar, 'foo'
+    push ar, 999
+    push ar, 'bar'
+    s1 = pop ar
+    print s1
+    s1 = pop ar
+    print s1
+    s1 = pop ar
+    print s1
+    print "\n"
+.end
+CODE
+bar999foo
+OUTPUT
+
+
+pir_output_is( << 'CODE', << 'OUTPUT', 'unshift/shift' );
+.HLL 'PHP', 'php_group'
+.sub _main
+    .local pmc ar, pmc_str
+    .local int i1
+    .local string s1
+
+    ar = new 'PHPArray'
+
+    unshift ar, 'foo'
+    s1 = shift ar
+    print s1
+    print "\n"
+.end
+CODE
+foo
+OUTPUT
+
+
+# Local Variables:
+#   mode: cperl
+#   cperl-indent-level: 4
+#   fill-column: 100
+# End:
+# vim: expandtab shiftwidth=4:
+



nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About