# This is a Perl5 file # ChangeLog # # 2005-09-06 # - tied() # # 2005-08-25 # - Arrays stored in a Scalar can be accessed using indexed store/fetch # # 2005-08-23 # - fixed Perl6::Cell::fetch # # 2005-08-19 # - Added roles (traits) - tieable, readonly # - Removed methods: .set_tieable(), .access() # These functions will be provided by traits. See t/trait.t. # Methods .tieable, .tie, .untie were kept, in order to keep # bound scalars tieable. # ._cell was promoted to public method, in order to keep .bind # working after a trait is applied. # # 2005-08-18 # - New methods: .tie($object), .untie, .tieable # - Method .id() returns the Cell id. # # 2005-08-17 # - New methods: .access('ro'); .access('rw'); .bind( $scalar ) # - reimplemented Scalar - auto-deref; new methods .fetch, .store, .unboxed # # 2005-08-15 # - created the 'Scalar' container # TODO - allow dual-behaviour objects with 'array_fetch' and 'hash_fetch' # TODO - separate 'whole_array_fetch' and 'array_element_fetch' (hash too) # TODO - lvalue substr # TODO - test tied Scalar # TODO - test 'readonly' and 'tieable' using traits - Scalar, Hash and Array # TODO - test Scalar auto-deref with a lazy List # TODO - verify .ref() and .undefine() implementations # TODO - .ref() should be inherited from Object # TODO - .meta should give access to .name, etc # TODO - 'is readonly' # TODO - store(Scalar) => store(Scalar->value) # TODO - store(Hash) / fetch # TODO - Constant class use strict; use Perl6::MetaModel; use Perl6::Value; use Carp; my $class_description = '-0.0.1-cpan:FGLOCK'; # Cell # # Cell is implemented as a native class # # cell keys: # v (value) (default: undef) # ro (default: r/w; 1=read-only cell) # tieable (default: non-tieable; 1=tieable) # tied (default: undef; can be set to an object) # type (default: undef; can be set to 'Hash' or 'Array') # id $Perl6::Cell::_id = rand; sub Perl6::Cell::new { bless { 'id' => ++$Perl6::Cell::_id }, 'Perl6::Cell' } sub Perl6::Cell::store { # warn "Perl6::Cell::store tied=$_[0]{tied} type=$_[0]{type} ref=".ref($_[0]{v})." param=@_"; die 'read only cell' if $_[0]{ro} && defined $_[0]{v}; # return $_[0]{tied}->store($_[1]) if $_[0]{tied}; if ( $_[0]{tied} ) { my $cell = shift; return $cell->{tied}->store( @_ ); } if ( @_ > 2 ) { # && ( # Perl6::Value::p6v_isa( $_[0]{v}, 'Array' ) || # Perl6::Value::p6v_isa( $_[0]{v}, 'Hash' ) # )) { my $cell = shift; # warn "storing @_ to ".ref($cell->{v}); # warn "Cell is not yet defined, and you are trying to access position $_[0] with @_" unless defined $cell->{v}; return $cell->{v}->store( @_ ); } $_[0]{v} = $_[1] } sub Perl6::Cell::fetch { # warn "Perl6::Cell::fetch tied=$_[0]{tied} type=$_[0]{type} ref=".ref($_[0]{v})." param=@_"; # return $_[0]{tied}->fetch if $_[0]{tied}; if ( $_[0]{tied} ) { my $cell = shift; return $cell->{tied}->fetch( @_ ); } if ( @_ > 1 ) { # && ( # Perl6::Value::p6v_isa( $_[0]{v}, 'Array' ) || # Perl6::Value::p6v_isa( $_[0]{v}, 'Hash' ) # )) { my $cell = shift; #warn "fetching @_ from ".ref($cell->{v}); return $cell->{v}->fetch( @_ ); } $_[0]{v} } sub Perl6::Cell::tie { die 'untieable cell' if ! $_[0]{tieable}; # TODO - use extra parameters $_[0]{tied} = $_[1]; } sub Perl6::Cell::untie { $_[0]{tied} = undef; } # --- end Cell # tieable, readonly # # These are Perl6 roles # See: S06 - Implementation types # See: t/trait.t # role tieable => { methods => { 'tieable' => sub { _('$:cell')->{tieable} = 1; 1; }, 'tie' => sub { shift; _('$:cell')->{tieable} = 1; _('$:cell')->tie(@_); }, 'untie' => sub { _('$:cell')->untie; }, } }; role readonly => { methods => { 'store' => sub { my ( $self, $value ) = @_; _('$:cell')->{ro} = 1; _('$:cell')->store($value ) }, } }; # Scalar # # Scalar is implemented as a Perl6 class # class1 'Scalar'.$class_description => { is => [ $::Object ], class => { attrs => [], methods => {} }, instance => { attrs => [ [ '$:cell' => { access => 'rw', build => sub { Perl6::Cell->new } } ] ], DESTROY => sub { # _('$:cell' => undef); # XXX - MM2.0 gc workaround }, methods => { 'fetch' => sub { shift; _('$:cell')->fetch( @_ ) }, 'store' => sub { # TODO - copy cell 'type' my $self = shift; my $value = shift; #carp "Too many arguments to Scalar->store: @_" if @_; if ( Perl6::Value::p6v_isa( $value, 'Scalar' ) ) { #carp "Attempting to store a Scalar into a Scalar"; $value = $value->cell->{v}; #warn "value was a $value"; } _('$:cell')->store( $value, @_ ); }, 'defined' => sub { Bit->new( '$.unboxed' => defined _('$:cell')->fetch ? 1 : 0 ) }, 'undefine' => sub { # XXX - didn't undefine the value # _('$.value' => undef) }, my $self = shift; _('$:cell')->store( undef ); # what happens if $x was bound to %h ??? _('$:cell')->{type} = undef; return $self; }, # adds support for @a[1].delete 'delete' => sub { (shift)->undefine }, # There is no 'set_access' - use 'is Readonly' instead # See: t/trait.t # 'access' => sub { # die "access must be 'ro' or 'rw'" # if $_[1] ne 'ro' && $_[1] ne 'rw'; # _('$:cell')->{ro} = $_[1] eq 'ro'; # return $::SELF; # }, 'bind' => sub { my ( $self, $scalar ) = @_; die "argument to bind() must be a Scalar" unless $scalar->isa( 'Scalar' ); _('$:cell', $scalar->cell); return $self; }, 'cell' => sub { _('$:cell') }, # _cell() is used by bind() 'id' => sub { _('$:cell')->{id} }, # There is no 'set_tieable' - use 'is Tieable' instead # See: t/trait.t # 'set_tieable' => sub { _('$:cell')->{tieable} = 1 }, 'tieable' => sub { _('$:cell')->{tieable} != 0 }, 'tie' => sub { shift; _('$:cell')->tie(@_) }, 'untie' => sub { _('$:cell')->untie }, 'tied' => sub { _('$:cell')->{tied} }, # See perl5/Perl6-MetaModel/t/14_AUTOLOAD.t 'isa' => sub { my($self,$cls)=@_; return 1 if $cls eq 'Scalar'; my $tmp = _('$:cell')->fetch; return 0 unless ref($tmp); defined $tmp ? $tmp->isa($cls) : 0; }, 'does' => sub { my($self,$cls)=@_; return 1 if $cls eq 'Scalar'; my $tmp = _('$:cell')->fetch; return 0 unless ref($tmp); defined $tmp ? $tmp->does($cls) : 0; }, 'AUTOLOAD' => sub { my ($self, @param) = @_; my $method = __('$AUTOLOAD'); my $tmp = _('$:cell')->fetch; if ( defined $tmp ) { if ( $method eq 'increment' || $method eq 'decrement' ) { _('$:cell')->store( $tmp->$method( @param ) ); return $::SELF; } # warn "calling Scalar -> $method on $tmp "; return Str->new( '$.unboxed' => $tmp ) unless ref( $tmp ); # unboxed return $tmp->$method( @param ); } else { # empty cell return Bit->new( '$.unboxed' => 0 ) if $method eq 'bit'; # XXX ? return $::CLASS if $method eq 'ref'; return Str->new( '$.unboxed' => '\\undef' ) if $method eq 'perl'; if ( $method eq 'increment' ) { _('$:cell')->store( Int->new( '$.unboxed' => 1 ) ); return $::SELF } if ( $method eq 'decrement' ) { _('$:cell')->store( Int->new( '$.unboxed' => -1 ) ); return $::SELF } return Str->new( '$.unboxed' => 'undef' ) if $method eq 'str'; return if $method eq 'unboxed'; die "Trying to call method \"$method\" in an undefined Scalar"; } }, }, } }; 1; __END__ =head1 NAME Perl6::Container::Scalar - Perl extension for Perl6 "Scalar" class =head1 SYNOPSIS use Perl6::Container::Scalar; ... =head1 DESCRIPTION ... =head1 SEE ALSO Pugs =head1 AUTHOR Flavio S. Glock, Efglock@gmail.com =head1 COPYRIGHT AND LICENSE Copyright (C) 2005 by Flavio S. Glock This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself, either Perl version 5.8.4 or, at your option, any later version of Perl 5 you may have available. =cut