The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
use v6-alpha;

use Test;

# L<S03/"Binding">

plan 37;

# Binding of hash elements.
# See thread "Binding of array elements" on p6l started by Ingo Blechschmidt:
# L<"http://www.nntp.perl.org/group/perl.perl6.language/22915">
{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  try { %hash<b> := $var };
  is %hash<b>, "d", "basic binding of a hash element (1)", :todo<feature>;
  unless %hash<b> eq "d" {
    skip_rest "Skipping binding of hash elements tests (not yet implemented in the normal runcore)";
    exit;
  }

  $var = "e";
  is %hash<b>, "e", "basic binding of a hash element (2)";

  %hash<b> = "f";
  is $var,     "f", "basic binding of a hash element (3)";
}

{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  %hash<b> := $var;
  $var      = "e";
  is %hash<b>, "e",             "binding of hash elements works with .delete (1)";

  %hash.delete("b");
  # $var unchanged, but assigning to $var doesn't modify @hash any
  # longer; similarily, changing @hash[1] doesn't modify $var now
  is $var,   "e",               "binding of hash elements works with .delete (2)";
  is ~%hash.values.sort, "x z", "binding of hash elements works with .delete (3)";

  $var     = "f";
  %hash<b> = "g";
  is $var,     "f",             "binding of hash elements works with .delete (4)";
  is %hash<b>, "g",             "binding of hash elements works with .delete (5)";
}

{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  %hash<b> := $var;
  $var      = "e";
  is %hash<b>, "e", "binding of hash elements works with resetting the hash (1)";

  %hash = ();
  # $var unchanged, but assigning to $var doesn't modify @hash any
  # longer; similarily, changing @hash[1] doesn't modify $var now
  is $var,   "e",   "binding of hash elements works with resetting the hash (2)";
  is ~%hash, "",    "binding of hash elements works with resetting the hash (3)";

  $var     = "f";
  %hash<b> = "g";
  is $var,     "f", "binding of hash elements works with resetting the hash (4)";
  is %hash<b>, "g", "binding of hash elements works with resetting the hash (5)";
}

{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  %hash<b> := $var;
  $var      = "e";
  is %hash<b>, "e", "binding of hash elements works with rebinding the hash (1)";

  my %other_hash = (:p<q>, :r<s>, :t<u>);
  %hash := %other_hash;
  # $var unchanged, but assigning to $var doesn't modify @hash any
  # longer; similarily, changing @hash[1] doesn't modify $var now
  is $var,    "e",  "binding of hash elements works with rebinding the hash (2)";
  is ~%hash.values.sort, "q s u",
    "binding of hash elements works with rebinding the hash (3)";

  $var     = "f";
  %hash<b> = "g";
  is $var,     "f", "binding of hash elements works with rebinding the hash (4)";
  is %hash<b>, "g", "binding of hash elements works with rebinding the hash (5)";
}

{
  my sub foo (%h) { %h<b> = "new_value" }

  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";
  %hash<b> := $var;

  foo %hash;
  is $var,    "new_value",     "passing a hash to a sub expecting a hash behaves correctly (1)";
  is ~%hash.values.sort, "new_value x z",
    "passing a hash to a sub expecting a hash behaves correctly (2)";
}

{
  my sub foo (Hash $h) { $h<b> = "new_value" }

  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";
  %hash<b> := $var;

  foo %hash;
  is $var, "new_value",
    "passing a hash to a sub expecting a hashref behaves correctly (1)";
  is ~%hash.values.sort, "new_value x z",
    "passing a hash to a sub expecting a hashref behaves correctly (2)";
}

# Binding of not yet existing elements should autovivify
{
  my %hash;
  my $var = "d";

  lives_ok { %hash<b> := $var },
                    "binding of not yet existing elements should autovivify (1)";
  is %hash<b>, "d", "binding of not yet existing elements should autovivify (2)";

  $var = "e";
  is %hash<b>, "e", "binding of not yet existing elements should autovivify (3)";
  is $var,     "e", "binding of not yet existing elements should autovivify (4)";
}

# Assignment (not binding) creates new containers
{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  %hash<b> := $var;
  $var      = "e";
  is %hash<b>, "e",                   "hash assignment creates new containers (1)";

  my %new_hash = %hash;
  $var         = "f";
  # %hash<b> and $var are now "f", but %new_hash is unchanged.
  is $var,                   "f",     "hash assignment creates new containers (2)";
  is ~%hash\   .values.sort, "f x z", "hash assignment creates new containers (3)";
  is ~%new_hash.values.sort, "e x z", "hash assignment creates new containers (4)";
}

# Binding does not create new containers
{
  my %hash  = (:a<x>, :b<y>, :c<z>);
  my $var   = "d";

  %hash<b> := $var;
  $var      = "e";
  is %hash<b>, "e",                   "hash binding does not create new containers (1)";

  my %new_hash := %hash;
  $var          = "f";
  # %hash<b> and $var are now "f", but %new_hash is unchanged.
  is $var,        "f",                "hash binding does not create new containers (2)";
  is ~%hash\   .values.sort, "f x z", "hash binding does not create new containers (3)";
  is ~%new_hash.values.sort, "f x z", "hash binding does not create new containers (4)";
}

# Binding %hash := $hashref.
# See
# http://colabti.de/irclogger/irclogger_log/perl6?date=2005-11-06,Sun&sel=388#l564
# and consider the magic behind parameter binding (which is really normal
# binding).
{
  my $hashref = { a => "a", b => "b" };
  my %hash   := $hashref;

  is +%hash, 2,                    'binding %hash := $hashref works (1)';

  %hash<b> = "c";
  is ~$hashref.values.sort, "a c", 'binding %hash := $hashref works (2)';
  is ~%hash\  .values.sort, "a c", 'binding %hash := $hashref works (3)';
}