Oh, and here's my test code for the Match PMC. This is really just a
copy of t/pmc/perlhash.t (since the Match PMC is supposed to behave
like a hash for the most part), but with one added test case at the
end showing how this would be used to store and retrieve
hypotheticals.
Index: t/pmc/match.t
===================================================================
RCS file: t/pmc/match.t
diff -N t/pmc/match.t
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ t/pmc/match.t 17 Aug 2004 17:28:17 -0000
@@ -0,0 +1,1256 @@
+#! perl
+
+# Copyright: 2001-2003 The Perl Foundation. All Rights Reserved.
+# $Id: match.t,v 1.44 2004/04/19 12:15:22 leo Exp $
+
+=head1 NAME
+
+t/pmc/match.t - Match Objects
+
+=head1 SYNOPSIS
+
+ % perl t/pmc/match.t
+
+=head1 DESCRIPTION
+
+Tests the C<Match> PMC. Does standard hashtable testing. Then tests
+various aspects of retrieving ranges of the input string.
+
+Probably ought to do nested match objects too.
+
+=cut
+
+use Parrot::Test tests => 34;
+use Test::More;
+
+output_is(<<CODE, <<OUTPUT, "Initial Match tests");
+ new P0, .Match
+
+ set P0["foo"], -7
+ set P0["bar"], 3.5
+ set P0["baz"], "value"
+
+ set I0, P0["foo"]
+ set N0, P0["bar"]
+ set S0, P0["baz"]
+
+ eq I0,-7,OK_1
+ print "not "
+OK_1: print "ok 1\\n"
+ eq N0,3.500000,OK_2
+ print N0
+OK_2: print "ok 2\\n"
+ eq S0,"value",OK_3
+ print S0
+OK_3: print "ok 3\\n"
+
+ set S1, "oof"
+ set S2, "rab"
+ set S3, "zab"
+
+ set P0[S1], 7
+ set P0[S2], -3.5
+ set P0[S3], "VALUE"
+
+ set I0, P0[S1]
+ set N0, P0[S2]
+ set S0, P0[S3]
+
+ eq I0,7,OK_4
+ print "not "
+OK_4: print "ok 4\\n"
+ eq N0,-3.500000,OK_5
+ print N0
+OK_5: print "ok 5\\n"
+ eq S0,"VALUE",OK_6
+ print S0
+OK_6: print "ok 6\\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+ok 5
+ok 6
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "more than one Match");
+ new P0, .Match
+ set S0, "key"
+ set P0[S0], 1
+
+ new P1, .Match
+ set S1, "another_key"
+ set P1[S1], 2
+
+ set I0, P0[S0]
+ set I1, P1[S1]
+
+ print I0
+ print "\n"
+ print I1
+ print "\n"
+ end
+CODE
+1
+2
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "hash keys with nulls in them");
+ new P0, .Match
+ set S0, "parp\0me"
+ set S1, "parp\0you"
+
+ set P0[S0], 1 # $P0{parp\0me} = 1
+ set P0[S1], 2 # $P0{parp\0you} = 2
+
+ set I0, P0[S0]
+ set I1, P0[S1]
+
+ print I0
+ print "\n"
+ print I1
+ print "\n"
+ end
+CODE
+1
+2
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "nearly the same hash keys");
+ new P0, .Match
+ set S0, "a\0"
+ set S1, "\0a"
+
+ set P0[S0], 1
+ set P0[S1], 2
+
+ set I0, P0[S0]
+ set I1, P0[S1]
+
+ print I0
+ print "\n"
+ print I1
+ print "\n"
+
+ end
+CODE
+1
+2
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "The same hash keys");
+ new P0, .Match
+ set S0, "Happy"
+ set S1, "Happy"
+
+ set P0[S0], 1
+ set I0, P0[S0]
+ print I0
+ print "\n"
+
+ set P0[S1], 2
+ set I1, P0[S1]
+
+ print I1
+ print "\n"
+
+ end
+CODE
+1
+2
+OUTPUT
+
+# NB Next test depends on "key2" hashing to zero, which it does with
+# the current algorithm; if the algorithm changes, change the test!
+
+output_is(<<'CODE', <<OUTPUT, "key that hashes to zero");
+ new P0, .Match
+ set S0, "key2"
+ set P0[S0], 1
+ set I0, P0[S0]
+ print I0
+ print "\n"
+ end
+CODE
+1
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "size of the hash");
+ new P0, .Match
+
+ set P0["0"], 1
+ set I0, P0
+ print I0
+ print "\n"
+
+ set P0["1"], 1
+ set I0, P0
+ print I0
+ print "\n"
+
+ set P0["0"], 1
+ set I0, P0
+ print I0
+ print "\n"
+
+ end
+CODE
+1
+2
+2
+OUTPUT
+
+output_is(<<CODE, <<OUTPUT, "stress test: loop(set, check)");
+ new P0, .Match
+
+ set I0, 200
+ set S0, "mikey"
+ set P0[S0], "base"
+ concat S1, S0, "s"
+ set P0[S1], "bases"
+ set S2, I0
+ concat S1, S0, S2
+ set P0[S1], "start"
+ set S3, P0["mikey"]
+ print S3
+ print "\\n"
+ set S3, P0["mikeys"]
+ print S3
+ print "\\n"
+ set S3, P0["mikey200"]
+ print S3
+ print "\\n"
+LOOP:
+ eq I0, 0, DONE
+ sub I0, I0, 1
+ set S2, I0
+ concat S1, S0, S2
+ concat S4, S0, S2
+ eq S1, S4, L1
+ print "concat mismatch: "
+ print S1
+ print " vs "
+ print S4
+ print "\\n"
+L1:
+ set P0[S1], I0
+ set I1, P0[S1]
+ eq I0, I1, L2
+ print "lookup mismatch: "
+ print I0
+ print " vs "
+ print I1
+ print "\\n"
+L2:
+ branch LOOP
+DONE:
+ set I0, P0["mikey199"]
+ print I0
+ print "\\n"
+ set I0, P0["mikey117"]
+ print I0
+ print "\\n"
+ set I0, P0["mikey1"]
+ print I0
+ print "\\n"
+ set I0, P0["mikey23"]
+ print I0
+ print "\\n"
+ set I0, P0["mikey832"]
+ print I0
+ print "\\n"
+ end
+CODE
+base
+bases
+start
+199
+117
+1
+23
+0
+OUTPUT
+
+# Check all values after setting all of them
+output_is(<<CODE, <<OUTPUT, "stress test: loop(set), loop(check)");
+ new P0, .Match
+
+ set I0, 200
+ set S0, "mikey"
+SETLOOP:
+ eq I0, 0, DONE
+ sub I0, I0, 1
+ set S2, I0
+ concat S1, S0, S2
+ set P0[S1], I0
+ branch SETLOOP
+
+ set I0, 200
+GETLOOP:
+ eq I0, 0, DONE
+ sub I0, I0, 1
+ set S2, I0
+ concat S1, S0, S2
+ set I1, P0[S1]
+ eq I0, I1, L2
+ print "lookup mismatch: "
+ print I0
+ print " vs "
+ print I1
+ print "\\n"
+L2:
+ branch GETLOOP
+
+DONE:
+ print "done\\n"
+ end
+CODE
+done
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Testing two hash indices with integers at a time");
+ new P0, .Match
+
+ set P0["foo"],37
+ set P0["bar"],-15
+
+ set I0,P0["foo"]
+ eq I0,37,OK_1
+ print "not "
+OK_1: print "ok 1\n"
+
+ set I0,P0["bar"]
+ eq I0,-15,OK_2
+ print "not "
+OK_2: print "ok 2\n"
+
+ set S1,"foo"
+ set I0,P0[S1]
+ eq I0,37,OK_3
+ print "not "
+OK_3: print "ok 3\n"
+
+ set S1,"bar"
+ set I0,P0[S1]
+ eq I0,-15,OK_4
+ print "not "
+OK_4: print "ok 4\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Testing two hash indices with numbers at a time");
+ new P0, .Match
+
+ set P0["foo"],37.100000
+ set P0["bar"],-15.100000
+
+ set N0,P0["foo"]
+ eq N0,37.100000,OK_1
+ print "not "
+OK_1: print "ok 1\n"
+
+ set N0,P0["bar"]
+ eq N0,-15.100000,OK_2
+ print "not "
+OK_2: print "ok 2\n"
+
+ set S1,"foo"
+ set N0,P0[S1]
+ eq N0,37.100000,OK_3
+ print "not "
+OK_3: print "ok 3\n"
+
+ set S1,"bar"
+ set N0,P0[S1]
+ eq N0,-15.100000,OK_4
+ print "not "
+OK_4: print "ok 4\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Testing two hash indices with strings at a time");
+ new P0, .Match
+
+ set P0["foo"],"baz"
+ set P0["bar"],"qux"
+
+ set S0,P0["foo"]
+ eq S0,"baz",OK_1
+ print "not "
+OK_1: print "ok 1\n"
+
+ set S0,P0["bar"]
+ eq S0,"qux",OK_2
+ print "not "
+OK_2: print "ok 2\n"
+
+ set S1,"foo"
+ set S0,P0[S1]
+ eq S0,"baz",OK_3
+ print "not "
+OK_3: print "ok 3\n"
+
+ set S1,"bar"
+ set S0,P0[S1]
+ eq S0,"qux",OK_4
+ print "not "
+OK_4: print "ok 4\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+
+# So far, we've only used INTVALs, FLOATVALs and STRINGs as values
+# and/or keys. Now we try PMCs.
+
+output_is(<<'CODE', <<OUTPUT, "Setting & getting scalar PMCs");
+ new P0, .Match
+ new P1, .PerlInt
+ new P2, .PerlInt
+
+ set S0, "non-PMC key"
+
+ set P1, 10
+ set P0[S0], P1
+ set P2, P0[S0]
+ eq P2, P1, OK1
+ print "not "
+OK1: print "ok 1\n"
+
+ set P1, -1234.000000
+ set P0[S0], P1
+ set P2, P0[S0]
+ eq P2, P1, OK2
+ print "not "
+OK2: print "ok 2\n"
+
+ set P1, "abcdefghijklmnopq"
+ set P0[S0], P1
+ set P2, P0[S0]
+ eq P2, P1, OK3
+ print "not "
+OK3: print "ok 3\n"
+
+ new P1, .PerlUndef
+ set P0[S0], P1
+ set P2, P0[S0]
+ typeof S1, P2
+ eq S1, "PerlUndef", OK4
+ print "not "
+OK4: print "ok 4\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Setting scalar PMCs & getting scalar values");
+ new P0, .Match
+ new P1, .PerlInt
+
+ set S0, "A rather large key"
+
+ set I0, 10
+ set P1, I0
+ set P0[S0], P1
+ set I1, P0[S0]
+ eq I1, I0, OK1
+ print "not "
+OK1: print "ok 1\n"
+
+ set N0, -1234.000000
+ set P1, N0
+ set P0[S0], P1
+ set N1, P0[S0]
+ eq N1, N0, OK2
+ print "not "
+OK2: print "ok 2\n"
+
+ set S1, "abcdefghijklmnopq"
+ set P1, S1
+ set P0[S0], P1
+ set S2, P0[S0]
+ eq S2, S1, OK3
+ print "not "
+OK3: print "ok 3\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Getting values from undefined keys");
+ new P2, .Match
+
+ set I0, P2["qwerty"]
+ set N0, P2["asdfgh"]
+ set S0, P2["zxcvbn"]
+ set P0, P2["123456"]
+
+ eq I0, 0, OK1
+ print "not "
+OK1: print "ok 1\n"
+
+ eq N0, 0.0, OK2
+ print "not "
+OK2: print "ok 2\n"
+
+ eq S0, "", OK3
+ print "not "
+OK3: print "ok 3\n"
+
+ typeof S1, P0
+ eq S1, "PerlUndef", OK4
+ print "not "
+OK4: print "ok 4\n"
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<CODE, <<OUTPUT, "Setting & getting non-scalar PMCs");
+ new P0,.Match
+ new P1,.PerlArray
+ new P2,.PerlArray
+ set P1[4],"string"
+ set P0["one"],P1
+ set P2,P0["one"]
+ set S0,P2[4]
+ print S0
+ print "\\n"
+ end
+CODE
+string
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Testing clone");
+ new P0, .Match
+ set S0, "a"
+ set P0[S0], S0
+ new P2, .PerlArray
+ set P2, 2
+ set P0["b"], P2
+
+ # P0 = { a => "a", b => [undef, undef] }
+
+ clone P1, P0
+ set P0["c"], 4
+ set P3, P0["b"]
+ set P3, 3
+ set P0["b"], P3
+ set P1["a"], "A"
+
+ # P0 = { a => "a", b => [undef, undef, undef], c => 4 }
+ # P1 = { a => "A", b => [undef, undef] }
+
+ set S0, P0["a"]
+ eq S0, "a", ok1
+ print "not "
+ok1:
+ print "ok 1\n"
+
+ set P5, P0["b"]
+ set I0, P5
+ eq I0, 3, ok2
+ print "not "
+ok2:
+ print "ok 2\n"
+
+ set I0, P0["c"]
+ eq I0, 4, ok3
+ print "not "
+ok3:
+ print "ok 3\n"
+
+ set S0, P1["a"]
+ eq S0, "A", ok4
+ print "not "
+ok4:
+ print "ok 4\n"
+
+ set P5, P1["b"]
+ set I0, P5
+ eq I0, 2, ok5
+ print "not ("
+ print I0
+ print ") "
+ok5:
+ print "ok 5\n"
+
+# XXX: this should return undef or something, but it dies instead.
+# set P3, P0["c"]
+# unless P3, ok6
+# print "not "
+# ok6:
+# print "ok 6\n"
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+ok 5
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Compound keys");
+ new P0, .Match
+ new P1, .Match
+ new P2, .PerlArray
+ set P1["b"], "ab"
+ set P0["a"], P1
+ set S0, P0["a";"b"]
+ eq S0, "ab", ok1
+ print "not "
+ok1:
+ print "ok 1\n"
+ set P2[20], 77
+ set P1["n"], P2
+ set I0, P0["a";"n";20]
+ eq I0, 77, ok2
+ print "not "
+ok2:
+ print "ok 2\n"
+ set S0, "a"
+ set S1, "n"
+ set I0, 20
+ set I0, P0[S0;S1;I0]
+ eq I0, 77, ok3
+ print "not "
+ok3:
+ print "ok 3\n"
+ set P0["c"], P2
+ set P2[33], P1
+ set S0, P0["c";33;"b"]
+ eq S0, "ab", ok4
+ print "not "
+ok4:
+ print "ok 4\n"
+ set S0, "c"
+ set I1, 33
+ set S2, "b"
+ set S0, P0[S0;I1;S2]
+ eq S0, "ab", ok5
+ print "not "
+ok5:
+ print "ok 5\n"
+ set P1["b"], 47.11
+ set N0, P0["c";I1;S2]
+ eq N0, 47.11, ok6
+ print "not "
+ok6:
+ print "ok 6\n"
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+ok 5
+ok 6
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Getting PMCs from compound keys");
+ new P0, .Match
+ new P1, .Match
+ new P2, .PerlInt
+ set P2, 12
+ set P1["b"], P2
+ set P0["a"], P1
+ set P3, P0["a";"b"]
+ print P3
+ print "\n"
+ end
+CODE
+12
+OUTPUT
+
+# A hash is only false if it has size 0
+
+output_is(<<'CODE', <<OUTPUT, "if (Match)");
+ new P0, .Match
+
+ if P0, BAD1
+ print "ok 1\n"
+ branch OK1
+BAD1: print "not ok 1\n"
+OK1:
+
+ set P0["key"], "value"
+ if P0, OK2
+ print "not "
+OK2: print "ok 2\n"
+
+ set P0["key"], ""
+ if P0, OK3
+ print "not "
+OK3: print "ok 3\n"
+
+ new P1, .PerlUndef
+ set P0["key"], P1
+ if P0, OK4
+ print "not "
+OK4: print "ok 4\n"
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "unless (Match)");
+ new P0, .Match
+
+ unless P0, OK1
+ print "not "
+OK1: print "ok 1\n"
+
+ set P0["key"], "value"
+ unless P0, BAD2
+ print "ok 2\n"
+ branch OK2
+BAD2: print "not ok 2"
+OK2:
+
+ set P0["key"], "\0"
+ unless P0, BAD3
+ print "ok 3\n"
+ branch OK3
+BAD3: print "not ok 3"
+OK3:
+
+ new P1, .PerlUndef
+ set P0["key"], P1
+ unless P0, BAD4
+ print "ok 4\n"
+ branch OK4
+BAD4: print "not ok 4"
+OK4:
+
+ end
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "defined");
+ new P0, .Match
+ defined I0, P0
+ print I0
+ print "\n"
+ defined I0, P1
+ print I0
+ print "\n"
+ set P0["a"], 1
+ defined I0, P0["a"]
+ print I0
+ print "\n"
+ defined I0, P0["b"]
+ print I0
+ print "\n"
+ new P1, .PerlUndef
+ set P0["c"], P1
+ defined I0, P0["c"]
+ print I0
+ print "\n"
+ end
+
+CODE
+1
+0
+1
+0
+0
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "exists");
+ new P0, .Match
+ set P0["a"], 1
+ exists I0, P0["a"]
+ print I0
+ print "\n"
+ exists I0, P0["b"]
+ print I0
+ print "\n"
+ new P1, .PerlUndef
+ set P0["c"], P1
+ exists I0, P0["c"]
+ print I0
+ print "\n"
+ end
+
+CODE
+1
+0
+1
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "delete");
+ new P0, .Match
+ set P0["a"], 1
+ exists I0, P0["a"]
+ print I0
+ print "\n"
+ delete P0["a"]
+ exists I0, P0["a"]
+ print I0
+ print "\n"
+ end
+CODE
+1
+0
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Cloning keys");
+ new P10, .Match
+ new P1, .Key
+
+ set P1, "Bar"
+ set P10[P1], "Food\n"
+ clone P2, P1
+ set S0, P10[P2]
+ print S0
+
+ set S1, "Baa"
+ set P10[S1], "Sheep\n"
+ clone S2, S1
+ set S0, P10[S2]
+ print S0
+
+ end
+CODE
+Food
+Sheep
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "Cloning PMC vals");
+ new P10, .Match
+ new P1, .PerlUndef
+ set P1, "value\n"
+ set P10["str"], P1
+ new P1, .PerlUndef
+ set P1, 42
+ set P10["int"], P1
+ clone P2, P10
+ set P0, P2["int"]
+ print P0
+ set P0, P2["str"]
+ print P0
+ end
+CODE
+42value
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "entry types - type_keyed");
+.include "pmctypes.pasm"
+ new P1, .Match
+ new P2, .PerlInt
+ set P1["pmc"], P2
+ typeof I0, P1["pmc"]
+ eq I0, .PerlInt, ok4
+ print "not "
+ok4:print "ok 4\n"
+ end
+CODE
+ok 4
+OUTPUT
+
+output_is(<<'CODE', <<OUTPUT, "delete and free_list");
+ set I2, 10
+ set I1, 1
+ new P0, .SArray
+ set P0, 1
+ new P1, .Match
+outer:
+ set P0[0], I1
+ sprintf S0, "ok %vd\n", P0
+ set P1[S0], S0
+ set I0, 100
+lp:
+ set P1["key"], 1
+ delete P1["key"]
+ dec I0
+ if I0, lp
+
+ set S1, P1[S0]
+ print S1
+ inc I1
+ le I1, I2, outer
+ set I0, P1
+ print I0
+ print "\n"
+ end
+
+CODE
+ok 1
+ok 2
+ok 3
+ok 4
+ok 5
+ok 6
+ok 7
+ok 8
+ok 9
+ok 10
+10
+OUTPUT
+
+output_is(<<'CODE', '', "reusing the undef");
+ new P0, .Match
+ set P1, P0["no"]
+ print P1
+ set P1, "one"
+ set P2, P0["nada"]
+ print P2
+ end
+CODE
+
+output_is(<<'CODE', <<OUTPUT, "exists with constant string key");
+ new P16, .Match
+ set P16["key1"], "value for key1\n"
+ set S16, P16["key1"]
+ print S16
+ set I16, 777777777
+ print I16
+ print "\n"
+ exists I17, P16["key1"]
+ print I17
+ print "\n"
+ exists I17, P16["no such"]
+ print I17
+ print "\n"
+ end
+
+CODE
+value for key1
+777777777
+1
+0
+OUTPUT
+
+output_is(<< 'CODE', << 'OUTPUT', "Match in PIR");
+##PIR##
+.sub _main
+ .local pmc hash1
+ hash1 = new Match
+ hash1['X'] = 'U'
+ .local string val1
+ val1 = hash1['X']
+ print val1
+ print "\n"
+ end
+.end
+CODE
+U
+OUTPUT
+
+output_is(<< 'CODE', << 'OUTPUT', "Setting with compound keys");
+##PIR##
+.sub _main
+ .local pmc outer_hash
+ outer_hash = new Match
+ .local pmc inner_hash
+ inner_hash = new Match
+ .local pmc inner_array
+ inner_array = new PerlArray
+ .local string elem_string
+ .local int elem_int
+ .local pmc elem_pmc
+ .local num elem_num
+
+ # setting and retrieving strings in an inner PerlArray
+ inner_array[128] = 'inner_array:128'
+ outer_hash['inner_array'] = inner_array
+ elem_string = outer_hash['inner_array';128]
+ print elem_string
+ print "\n"
+ outer_hash['inner_array';128] = 'changed inner_array:128'
+ elem_string = outer_hash['inner_array';128]
+ print elem_string
+ print "\n"
+
+ # setting and retrieving strings in an inner Match
+ inner_hash['129'] = 'inner_hash:129'
+ outer_hash['inner_hash'] = inner_hash
+ elem_string = outer_hash['inner_hash';'129']
+ print elem_string
+ print "\n"
+ outer_hash['inner_hash';'129'] = 'changed inner_hash:129'
+ elem_string = outer_hash['inner_hash';'129']
+ print elem_string
+ print "\n"
+
+ # setting and retrieving integer in an inner PerlArray
+ inner_array[130] = 130
+ outer_hash['inner_array'] = inner_array
+ elem_int = outer_hash['inner_array';130]
+ print elem_int
+ print "\n"
+ outer_hash['inner_array';130] = -130
+ elem_int = outer_hash['inner_array';130]
+ print elem_int
+ print "\n"
+
+ # setting and retrieving integer in an inner Match
+ inner_hash['131'] = 131
+ outer_hash['inner_hash'] = inner_hash
+ elem_int = outer_hash['inner_hash';'131']
+ print elem_int
+ print "\n"
+ outer_hash['inner_hash';'131'] = -131
+ elem_int = outer_hash['inner_hash';'131']
+ print elem_int
+ print "\n"
+
+ # setting and retrieving a PMC in an inner PerlArray
+ .local pmc in_pmc
+ in_pmc = new PerlString
+ in_pmc = 'inner_array:132'
+ inner_array[132] = in_pmc
+ outer_hash['inner_array'] = inner_array
+ elem_pmc = outer_hash['inner_array';132]
+ print elem_pmc
+ print "\n"
+ in_pmc = 'changed inner_array:132'
+ outer_hash['inner_array';132] = in_pmc
+ elem_pmc = outer_hash['inner_array';132]
+ print elem_pmc
+ print "\n"
+
+ # setting and retrieving a PMC in an inner Match
+ in_pmc = 'inner_array:133'
+ inner_hash['133'] = in_pmc
+ outer_hash['inner_hash'] = inner_hash
+ elem_string = outer_hash['inner_hash';'133']
+ print elem_string
+ print "\n"
+ in_pmc = 'changed inner_hash:133'
+ outer_hash['inner_hash';'133'] = in_pmc
+ elem_string = outer_hash['inner_hash';'133']
+ print elem_string
+ print "\n"
+
+ # setting and retrieving a float in an inner PerlArray
+ inner_array[134] = 134.134
+ outer_hash['inner_array'] = inner_array
+ elem_num = outer_hash['inner_array';134]
+ print elem_num
+ print "\n"
+ outer_hash['inner_array';134] = -134.134
+ elem_num = outer_hash['inner_array';134]
+ print elem_num
+ print "\n"
+
+ # setting and retrieving a float in an inner Match
+ inner_hash['135'] = 135.135
+ outer_hash['inner_hash'] = inner_hash
+ elem_num = outer_hash['inner_hash';'135']
+ print elem_num
+ print "\n"
+ outer_hash['inner_hash';'135'] = -135.135
+ elem_num = outer_hash['inner_hash';'135']
+ print elem_num
+ print "\n"
+
+ end
+.end
+CODE
+inner_array:128
+changed inner_array:128
+inner_hash:129
+changed inner_hash:129
+130
+-130
+131
+-131
+inner_array:132
+changed inner_array:132
+inner_array:133
+changed inner_hash:133
+134.134000
+-134.134000
+135.135000
+-135.135000
+OUTPUT
+
+output_is(<< 'CODE', << 'OUTPUT', "mutating the lookup string");
+ new P0, .Match
+ set P0["a"], "one"
+ set P0["ab"], "two"
+ set P0["abc"], "three"
+
+ set S0, "a"
+ set S1, P0[S0]
+ print S1
+ print "\n"
+
+ concat S0, "b"
+ set S1, P0[S0]
+ print S1
+ print "\n"
+
+ concat S0, "c"
+ set S1, P0[S0]
+ print S1
+ print "\n"
+
+ end
+CODE
+one
+two
+three
+OUTPUT
+
+output_is(<< 'CODE', << 'OUTPUT', "empty and full-string matches");
+ new P0, .Match
+ set P0["!INPUT"], "the full input string"
+
+ new P1, .MatchRange
+ set P1[0], 0
+ set P1[1], -1
+ set P0["empty_at_start"], P1
+
+ new P1, .MatchRange
+ set P1[0], 5
+ set P1[1], 4
+ set P0["empty_at_middle"], P1
+
+ new P1, .MatchRange
+ set P1[0], 0
+ set P1[1], 20
+ set P0["whole"], P1
+
+ new P1, .MatchRange
+ set P1[0], 4
+ set P1[1], 7
+ set P0["full"], P1
+
+ new P1, .MatchRange
+ set P1[0], -2
+ set P1[1], 7
+ set P0["no_start"], P1
+
+ new P1, .MatchRange
+ set P1[0], 3
+ set P1[1], -2
+ set P0["no_end"], P1
+
+ set P0["regular_key"], "boring old value"
+
+ new P1, .Match
+ set P1["subrule"], P0
+
+ set S0, "empty_at_start"
+ bsr test
+ set S0, "empty_at_middle"
+ bsr test
+ set S0, "whole"
+ bsr test
+ set S0, "full"
+ bsr test
+ set S0, "no_start"
+ bsr test
+ set S0, "no_end"
+ bsr test
+ set S0, "regular_key"
+ bsr test
+
+ set S0, "empty_at_start"
+ bsr subtest
+ set S0, "empty_at_middle"
+ bsr subtest
+ set S0, "whole"
+ bsr subtest
+ set S0, "full"
+ bsr subtest
+ set S0, "no_start"
+ bsr subtest
+ set S0, "no_end"
+ bsr subtest
+ set S0, "regular_key"
+ bsr subtest
+
+ print "Direct access to start,end for 'full': "
+ set I0, P0["full";0]
+ print I0
+ print ","
+ set I0, P0["full";1]
+ print I0
+ print "\n"
+
+ end
+
+test:
+ print S0
+ print ":"
+ set S0, P0[S0]
+ isnull S0, report_null
+ print S0
+ print "\n"
+ ret
+report_null:
+ print "<null>\n"
+ ret
+
+subtest:
+ print "subrule/"
+ print S0
+ print ":"
+ set S0, P1["subrule";S0]
+ isnull S0, report_null
+ print S0
+ print "\n"
+ ret
+subreport_null:
+ print "<null>\n"
+ ret
+CODE
+empty_at_start:
+empty_at_middle:
+whole:the full input string
+full:full
+no_start:<null>
+no_end:<null>
+regular_key:boring old value
+subrule/empty_at_start:
+subrule/empty_at_middle:
+subrule/whole:the full input string
+subrule/full:full
+subrule/no_start:<null>
+subrule/no_end:<null>
+subrule/regular_key:boring old value
+Direct access to start,end for 'full': 4,7
+OUTPUT
+
+1;