package YAML::Types; use YAML::Mo; use YAML::Node; # XXX These classes and their APIs could still use some refactoring, # but at least they work for now. #------------------------------------------------------------------------------- package YAML::Type::blessed; use YAML::Mo; # XXX sub yaml_dump { my $self = shift; my ($value) = @_; my ($class, $type) = YAML::Mo::Object->node_info($value); no strict 'refs'; my $kind = lc($type) . ':'; my $tag = ${$class . '::ClassTag'} || "!perl/$kind$class"; if ($type eq 'REF') { YAML::Node->new( {(&YAML::VALUE, ${$_[0]})}, $tag ); } elsif ($type eq 'SCALAR') { $_[1] = $$value; YAML::Node->new($_[1], $tag); } elsif ($type eq 'GLOB') { # blessed glob support is minimal, and will not round-trip # initial aim: to not cause an error return YAML::Type::glob->yaml_dump($value, $tag); } else { YAML::Node->new($value, $tag); } } #------------------------------------------------------------------------------- package YAML::Type::undef; sub yaml_dump { my $self = shift; } sub yaml_load { my $self = shift; } #------------------------------------------------------------------------------- package YAML::Type::glob; sub yaml_dump { my $self = shift; # $_[0] remains as the glob my $tag = pop @_ if 2==@_; $tag = '!perl/glob:' unless defined $tag; my $ynode = YAML::Node->new({}, $tag); for my $type (qw(PACKAGE NAME SCALAR ARRAY HASH CODE IO)) { my $value = *{$_[0]}{$type}; $value = $$value if $type eq 'SCALAR'; if (defined $value) { if ($type eq 'IO') { my @stats = qw(device inode mode links uid gid rdev size atime mtime ctime blksize blocks); undef $value; $value->{stat} = YAML::Node->new({}); if ($value->{fileno} = fileno(*{$_[0]})) { local $^W; map {$value->{stat}{shift @stats} = $_} stat(*{$_[0]}); $value->{tell} = tell(*{$_[0]}); } } $ynode->{$type} = $value; } } return $ynode; } sub yaml_load { my $self = shift; my ($node, $class, $loader) = @_; my ($name, $package); if (defined $node->{NAME}) { $name = $node->{NAME}; delete $node->{NAME}; } else { $loader->warn('YAML_LOAD_WARN_GLOB_NAME'); return undef; } if (defined $node->{PACKAGE}) { $package = $node->{PACKAGE}; delete $node->{PACKAGE}; } else { $package = 'main'; } no strict 'refs'; if (exists $node->{SCALAR}) { *{"${package}::$name"} = \$node->{SCALAR}; delete $node->{SCALAR}; } for my $elem (qw(ARRAY HASH CODE IO)) { if (exists $node->{$elem}) { if ($elem eq 'IO') { $loader->warn('YAML_LOAD_WARN_GLOB_IO'); delete $node->{IO}; next; } *{"${package}::$name"} = $node->{$elem}; delete $node->{$elem}; } } for my $elem (sort keys %$node) { $loader->warn('YAML_LOAD_WARN_BAD_GLOB_ELEM', $elem); } return *{"${package}::$name"}; } #------------------------------------------------------------------------------- package YAML::Type::code; my $dummy_warned = 0; my $default = '{ "DUMMY" }'; sub yaml_dump { my $self = shift; my $code; my ($dumpflag, $value) = @_; my ($class, $type) = YAML::Mo::Object->node_info($value); my $tag = "!perl/code"; $tag .= ":$class" if defined $class; if (not $dumpflag) { $code = $default; } else { bless $value, "CODE" if $class; eval { use B::Deparse }; return if $@; my $deparse = B::Deparse->new(); eval { local $^W = 0; $code = $deparse->coderef2text($value); }; if ($@) { warn YAML::YAML_DUMP_WARN_DEPARSE_FAILED() if $^W; $code = $default; } bless $value, $class if $class; chomp $code; $code .= "\n"; } $_[2] = $code; YAML::Node->new($_[2], $tag); } sub yaml_load { my $self = shift; my ($node, $class, $loader) = @_; if ($loader->load_code) { my $code = eval "package main; sub $node"; if ($@) { $loader->warn('YAML_LOAD_WARN_PARSE_CODE', $@); return sub {}; } else { CORE::bless $code, $class if $class; return $code; } } else { return CORE::bless sub {}, $class if $class; return sub {}; } } #------------------------------------------------------------------------------- package YAML::Type::ref; sub yaml_dump { my $self = shift; YAML::Node->new({(&YAML::VALUE, ${$_[0]})}, '!perl/ref') } sub yaml_load { my $self = shift; my ($node, $class, $loader) = @_; $loader->die('YAML_LOAD_ERR_NO_DEFAULT_VALUE', 'ptr') unless exists $node->{&YAML::VALUE}; return \$node->{&YAML::VALUE}; } #------------------------------------------------------------------------------- package YAML::Type::regexp; # XXX Be sure to handle blessed regexps (if possible) sub yaml_dump { die "YAML::Type::regexp::yaml_dump not currently implemented"; } use constant _QR_TYPES => { '' => sub { qr{$_[0]} }, x => sub { qr{$_[0]}x }, i => sub { qr{$_[0]}i }, s => sub { qr{$_[0]}s }, m => sub { qr{$_[0]}m }, ix => sub { qr{$_[0]}ix }, sx => sub { qr{$_[0]}sx }, mx => sub { qr{$_[0]}mx }, si => sub { qr{$_[0]}si }, mi => sub { qr{$_[0]}mi }, ms => sub { qr{$_[0]}sm }, six => sub { qr{$_[0]}six }, mix => sub { qr{$_[0]}mix }, msx => sub { qr{$_[0]}msx }, msi => sub { qr{$_[0]}msi }, msix => sub { qr{$_[0]}msix }, }; sub yaml_load { my $self = shift; my ($node, $class) = @_; return qr{$node} unless $node =~ /^\(\?([\^\-xism]*):(.*)\)\z/s; my ($flags, $re) = ($1, $2); $flags =~ s/-.*//; $flags =~ s/^\^//; my $sub = _QR_TYPES->{$flags} || sub { qr{$_[0]} }; my $qr = &$sub($re); bless $qr, $class if length $class; return $qr; } 1;