]> git.vpit.fr Git - perl/modules/autovivification.git/commitdiff
Handle array and hash slices
authorVincent Pit <vince@profvince.com>
Wed, 24 Jun 2009 16:50:42 +0000 (18:50 +0200)
committerVincent Pit <vince@profvince.com>
Wed, 24 Jun 2009 16:50:42 +0000 (18:50 +0200)
autovivification.xs
samples/hash2array.pl
t/20-hash.t
t/30-array.t
t/31-array-fast.t
t/lib/autovivification/TestCases.pm

index 688669372bcf96029dacc40cbd7b5efcee721eca..ae8db121e2527d6c57bfdc0b0511111068ecb857 100644 (file)
@@ -409,7 +409,7 @@ STATIC OP *a_pp_rv2av(pTHX) {
 
 /* ... pp_rv2hv ............................................................ */
 
-STATIC OP *a_pp_rv2hv(pTHX) {
+STATIC OP *a_pp_rv2hv_simple(pTHX) {
  a_op_info oi;
  UV flags;
  dSP;
@@ -427,6 +427,29 @@ STATIC OP *a_pp_rv2hv(pTHX) {
  return CALL_FPTR(oi.old_pp)(aTHX);
 }
 
+STATIC OP *a_pp_rv2hv(pTHX) {
+ a_op_info oi;
+ UV flags;
+ dSP;
+
+ a_map_fetch(PL_op, &oi);
+ flags = oi.flags;
+
+ if (flags & A_HINT_DEREF) {
+  if (!SvOK(TOPs)) {
+   SV *hv;
+   POPs;
+   hv = sv_2mortal((SV *) newHV());
+   PUSHs(hv);
+   RETURN;
+  }
+ } else {
+  PL_op->op_ppaddr = oi.old_pp;
+ }
+
+ return CALL_FPTR(oi.old_pp)(aTHX);
+}
+
 /* ... pp_deref (aelem,helem,rv2sv,padsv) .................................. */
 
 STATIC OP *a_pp_deref(pTHX) {
@@ -626,7 +649,7 @@ STATIC OP *a_ck_deref(pTHX_ OP *o) {
   case OP_HELEM:
    old_ck = a_old_ck_helem;
    if ((hint & A_HINT_DO) && !(hint & A_HINT_STRICT))
-    a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv);
+    a_recheck_rv2xv(cUNOPo->op_first, OP_RV2HV, a_pp_rv2hv_simple);
    break;
   case OP_RV2SV:
    old_ck = a_old_ck_rv2sv;
@@ -660,7 +683,7 @@ STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
 
  switch (o->op_type) {
   case OP_RV2AV: old_ck = a_old_ck_rv2av; new_pp = a_pp_rv2av; break;
-  case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv; break;
+  case OP_RV2HV: old_ck = a_old_ck_rv2hv; new_pp = a_pp_rv2hv_simple; break;
  }
  o = CALL_FPTR(old_ck)(aTHX_ o);
 
@@ -677,6 +700,40 @@ STATIC OP *a_ck_rv2xv(pTHX_ OP *o) {
  return o;
 }
 
+/* ... ck_xslice (aslice,hslice) ........................................... */
+
+/* I think those are only found at the root, but there's nothing that really
+ * prevent them to be inside the expression too. We only need to update the
+ * root so that the rest of the expression will see the right context when
+ * resolving. That's why we don't replace the ppaddr. */
+
+STATIC OP *(*a_old_ck_aslice)(pTHX_ OP *) = 0;
+STATIC OP *(*a_old_ck_hslice)(pTHX_ OP *) = 0;
+
+STATIC OP *a_ck_xslice(pTHX_ OP *o) {
+ OP * (*old_ck)(pTHX_ OP *o) = 0;
+ UV hint = a_hint();
+
+ switch (o->op_type) {
+  case OP_ASLICE:
+   old_ck = a_old_ck_aslice;
+   break;
+  case OP_HSLICE:
+   old_ck = a_old_ck_hslice;
+   if (hint & A_HINT_DO)
+    a_recheck_rv2xv(cUNOPo->op_first->op_sibling, OP_RV2HV, a_pp_rv2hv);
+   break;
+ }
+ o = CALL_FPTR(old_ck)(aTHX_ o);
+
+ if (hint & A_HINT_DO) {
+  a_map_store_root(o, 0, hint);
+ } else
+  a_map_delete(o);
+
+ return o;
+}
+
 /* ... ck_root (exists,delete,keys,values) ................................. */
 
 /* Those ops are only found at the root of a dereferencing expression. We can
@@ -768,6 +825,11 @@ BOOT:
   a_old_ck_rv2hv      = PL_check[OP_RV2HV];
   PL_check[OP_RV2HV]  = MEMBER_TO_FPTR(a_ck_rv2xv);
 
+  a_old_ck_aslice     = PL_check[OP_ASLICE];
+  PL_check[OP_ASLICE] = MEMBER_TO_FPTR(a_ck_xslice);
+  a_old_ck_hslice     = PL_check[OP_HSLICE];
+  PL_check[OP_HSLICE] = MEMBER_TO_FPTR(a_ck_xslice);
+
   a_old_ck_exists     = PL_check[OP_EXISTS];
   PL_check[OP_EXISTS] = MEMBER_TO_FPTR(a_ck_root);
   a_old_ck_delete     = PL_check[OP_DELETE];
index 8b3419d1139fbdbb68f742470b85125bbc8aec40..e70b69be8ea90e83c558a527dcc862c8a0beba67 100644 (file)
@@ -10,7 +10,10 @@ open my $hash_t,       '<', 't/20-hash.t';
 open my $array_t,      '>', 't/30-array.t';
 open my $array_fast_t, '>', 't/31-array-fast.t';
 
-sub num { ord($_[0]) - ord('a') }
+sub num {
+ my ($char) = $_[0] =~ /['"]?([a-z])['"]?/;
+ return ord($char) - ord('a')
+}
 
 sub hash2array {
  my ($h) = @_;
@@ -46,7 +49,12 @@ while (<$hash_t>) {
   for my $file ([ 1, $array_t ], [ 0, $array_fast_t ]) {
    local $_ = $_;
    s!(\ba\b)?(\s*)HASH\b!($1 ? 'an': '') . "$2ARRAY"!eg;
-   s!->{([a-z])}!my $n = num($1); '->[' . ($file->[0] ? "\$N[$n]" : $n) .']'!eg;
+   s{
+    {\s*(['"]?[a-z]['"]?(?:\s*,\s*['"]?[a-z]['"]?)*)\s*}
+   }{
+    '[' . join(', ', map { my $n = num($_); $file->[0] ? "\$N[$n]" : $n }
+                      split /\s*,\s*/, $1) . ']'
+   }gex;
    s!%(\{?)\$!\@$1\$!g;
    my $buf;
    my $suffix = $_;
index fa16a375d6cadae30f4499fcfbe3ceece2223dbf..6438056f89dae2ea5cf53250540d8e86ed9ea824 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 9 * 3 * 274;
+use Test::More tests => 9 * 3 * 290;
 
 use lib 't/lib';
 use autovivification::TestCases;
@@ -130,6 +130,28 @@ $x->{a} = 1 # my @a = %$x; () # '', undef, { a => 1 } # +exists
 $x->{a} = 1 # my @a = %$x; () # '', undef, { a => 1 } # +delete
 $x->{a} = 1 # my @a = %$x; () # '', undef, { a => 1 } # +store
 
+--- slice ---
+
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], { }
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], undef #
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], undef # +fetch
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], { }   # +exists
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], { }   # +delete
+$x # my @a = @$x{'a', 'b'}; \@a # '', [ undef, undef ], { }   # +store
+
+$x->{b} = 0 # my @a = @$x{'a', 'b'}; \@a # '', [ undef, 0 ], { b => 0 } # +fetch
+
+$x # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 }
+$x # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } #
+$x # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } # +fetch
+$x # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } # +exists
+$x # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } # +delete
+$x # @$x{'a', 'b'} = (1, 2); () # qr/^Can't vivify reference/, undef, undef # +store
+
+$x->{a} = 0              # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } # +store
+$x->{c} = 0              # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2, c => 0 } # +store
+$x->{a} = 0, $x->{b} = 0 # @$x{'a', 'b'} = (1, 2); () # '', undef, { a => 1, b => 2 } # +store
+
 --- exists ---
 
 $x # exists $x->{a} # '', '', { }
index feea9ebfee9b7218481784004ae4b5b7cff08cca..d8497f3f9a371291f49888be5082ed4e017b75be 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 9 * 3 * 274;
+use Test::More tests => 9 * 3 * 290;
 
 use lib 't/lib';
 use autovivification::TestCases;
@@ -130,6 +130,28 @@ $x->[$N[0]] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +exists
 $x->[$N[0]] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +delete
 $x->[$N[0]] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +store
 
+--- slice ---
+
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], [ ]
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], undef #
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], undef # +fetch
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], [ ] # +exists
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], [ ] # +delete
+$x # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, undef ], [ ] # +store
+
+$x->[$N[1]] = 0 # my @a = @$x[$N[0], $N[1]]; \@a # '', [ undef, 0 ], [ undef, 0 ] # +fetch
+
+$x # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ]
+$x # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] #
+$x # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] # +fetch
+$x # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] # +exists
+$x # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] # +delete
+$x # @$x[$N[0], $N[1]] = (1, 2); () # qr/^Can't vivify reference/, undef, undef # +store
+
+$x->[$N[0]] = 0 # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] # +store
+$x->[$N[2]] = 0 # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2, 0 ] # +store
+$x->[$N[0]] = 0, $x->[$N[1]] = 0 # @$x[$N[0], $N[1]] = (1, 2); () # '', undef, [ 1, 2 ] # +store
+
 --- exists ---
 
 $x # exists $x->[$N[0]] # '', '', [ ]
index 1adab77b6c6bdcbf88f799b1812f863d19d6c8ce..6f620407922dbbce6563f07c048efc9c90941171 100644 (file)
@@ -3,7 +3,7 @@
 use strict;
 use warnings;
 
-use Test::More tests => 9 * 3 * 274;
+use Test::More tests => 9 * 3 * 290;
 
 use lib 't/lib';
 use autovivification::TestCases;
@@ -130,6 +130,28 @@ $x->[0] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +exists
 $x->[0] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +delete
 $x->[0] = 1 # my @a = @$x; () # '', undef, [ 1 ] # +store
 
+--- slice ---
+
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], [ ]
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], undef #
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], undef # +fetch
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], [ ] # +exists
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], [ ] # +delete
+$x # my @a = @$x[0, 1]; \@a # '', [ undef, undef ], [ ] # +store
+
+$x->[1] = 0 # my @a = @$x[0, 1]; \@a # '', [ undef, 0 ], [ undef, 0 ] # +fetch
+
+$x # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ]
+$x # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] #
+$x # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] # +fetch
+$x # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] # +exists
+$x # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] # +delete
+$x # @$x[0, 1] = (1, 2); () # qr/^Can't vivify reference/, undef, undef # +store
+
+$x->[0] = 0 # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] # +store
+$x->[2] = 0 # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2, 0 ] # +store
+$x->[0] = 0, $x->[1] = 0 # @$x[0, 1] = (1, 2); () # '', undef, [ 1, 2 ] # +store
+
 --- exists ---
 
 $x # exists $x->[0] # '', '', [ ]
index 99d6672a2984d6cc51494108e7998371cd2da4a1..13feb5a6c784561786d99cb7911aa64be366f27b 100644 (file)
@@ -41,9 +41,11 @@ TESTCASE
 sub testcase_ok {
  local $_  = shift;
  my $sigil = shift;
+
  my @chunks = split /#+/, "$_ ";
  s/^\s+//, s/\s+$// for @chunks;
  my ($init, $code, $exp, $opts) = @chunks;
+
  (my $var = $init) =~ s/[^\$@%\w].*//;
  $init = $var eq $init ? '' : "$init;";
  my $use;
@@ -60,15 +62,19 @@ sub testcase_ok {
   $opts = 'default';
   $use  = '';
  }
+
  my @base = ([ $var, $init, $code, $exp, $use ]);
  if ($var =~ /\$/) {
+  my ($name) = $var =~ /^\$(.*)/;
+
   my @oldderef = @{$base[0]};
   $oldderef[2] =~ s/\Q$var\E\->/\$$var/g;
   push @base, \@oldderef;
 
   my @nonref = @{$base[0]};
-  $nonref[0] =~ s/^\$/$sigil/;
+  $nonref[0] = $sigil . $name;
   for ($nonref[1], $nonref[2]) {
+   s/\@\Q$var\E([\[\{])/\@$name$1/g;
    s/\Q$sigil$var\E/$nonref[0]/g;
    s/\Q$var\E\->/$var/g;
   }
@@ -87,12 +93,14 @@ sub testcase_ok {
   $nonref[3] =~ s/,\s*undef\s*$/, $empty/;
   push @base, \@nonref;
  }
+
  my @testcases = map {
   my ($var, $init, $code, $exp, $use) = @$_;
   [ $var, $init,               $code, $exp, $use, $opts, 0 ],
   [ $var, "use strict; $init", $code, $exp, $use, $opts, 1 ],
   [ $var, "no strict;  $init", $code, $exp, $use, $opts, 1 ],
  } @base;
+
  for (@testcases) {
   my ($testcase, $desc) = generate(@$_);
   my @N = (0 .. 9);