D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
usr
/
local
/
share
/
perl5
/
PPI
/
Token
/
_QuoteEngine
/
Filename :
Full.pm
back
Copy
package PPI::Token::_QuoteEngine::Full; # Full quote engine use strict; use Clone (); use Carp (); use PPI::Token::_QuoteEngine (); use vars qw{$VERSION @ISA %quotes %sections}; BEGIN { $VERSION = '1.236'; @ISA = 'PPI::Token::_QuoteEngine'; # Prototypes for the different braced sections %sections = ( '(' => { type => '()', _close => ')' }, '<' => { type => '<>', _close => '>' }, '[' => { type => '[]', _close => ']' }, '{' => { type => '{}', _close => '}' }, ); # For each quote type, the extra fields that should be set. # This should give us faster initialization. %quotes = ( 'q' => { operator => 'q', braced => undef, separator => undef, _sections => 1 }, 'qq' => { operator => 'qq', braced => undef, separator => undef, _sections => 1 }, 'qx' => { operator => 'qx', braced => undef, separator => undef, _sections => 1 }, 'qw' => { operator => 'qw', braced => undef, separator => undef, _sections => 1 }, 'qr' => { operator => 'qr', braced => undef, separator => undef, _sections => 1, modifiers => 1 }, 'm' => { operator => 'm', braced => undef, separator => undef, _sections => 1, modifiers => 1 }, 's' => { operator => 's', braced => undef, separator => undef, _sections => 2, modifiers => 1 }, 'tr' => { operator => 'tr', braced => undef, separator => undef, _sections => 2, modifiers => 1 }, # Y is the little-used variant of tr 'y' => { operator => 'y', braced => undef, separator => undef, _sections => 2, modifiers => 1 }, '/' => { operator => undef, braced => 0, separator => '/', _sections => 1, modifiers => 1 }, # Angle brackets quotes mean "readline(*FILEHANDLE)" '<' => { operator => undef, braced => 1, separator => undef, _sections => 1, }, # The final ( and kind of depreciated ) "first match only" one is not # used yet, since I'm not sure on the context differences between # this and the trinary operator, but it's here for completeness. '?' => { operator => undef, braced => 0, separator => '?', _sections => 1, modifiers => 1 }, ); } sub new { my $class = shift; my $init = defined $_[0] ? shift : Carp::croak("::Full->new called without init string"); # Create the token ### This manual SUPER'ing ONLY works because none of ### Token::Quote, Token::QuoteLike and Token::Regexp ### implement a new function of their own. my $self = PPI::Token::new( $class, $init ) or return undef; # Do we have a prototype for the initializer? If so, add the extra fields my $options = $quotes{$init} or return $self->_error( "Unknown quote type '$init'" ); foreach ( keys %$options ) { $self->{$_} = $options->{$_}; } # Set up the modifiers hash if needed $self->{modifiers} = {} if $self->{modifiers}; # Handle the special < base if ( $init eq '<' ) { $self->{sections}->[0] = Clone::clone( $sections{'<'} ); } $self; } sub _fill { my $class = shift; my $t = shift; my $self = $t->{token} or Carp::croak("::Full->_fill called without current token"); # Load in the operator stuff if needed if ( $self->{operator} ) { # In an operator based quote-like, handle the gap between the # operator and the opening separator. if ( substr( $t->{line}, $t->{line_cursor}, 1 ) =~ /\s/ ) { # Go past the gap my $gap = $self->_scan_quote_like_operator_gap( $t ); return undef unless defined $gap; if ( ref $gap ) { # End of file $self->{content} .= $$gap; return 0; } $self->{content} .= $gap; } # The character we are now on is the separator. Capture, # and advance into the first section. my $sep = substr( $t->{line}, $t->{line_cursor}++, 1 ); $self->{content} .= $sep; # Determine if these are normal or braced type sections if ( my $section = $sections{$sep} ) { $self->{braced} = 1; $self->{sections}->[0] = Clone::clone($section); } else { $self->{braced} = 0; $self->{separator} = $sep; } } # Parse different based on whether we are normal or braced my $rv = $self->{braced} ? $self->_fill_braced($t) : $self->_fill_normal($t); return $rv if !$rv; # Return now unless it has modifiers ( i.e. s/foo//eieio ) return 1 unless $self->{modifiers}; # Check for modifiers my $char; my $len = 0; while ( ($char = substr( $t->{line}, $t->{line_cursor} + 1, 1 )) =~ /[^\W\d_]/ ) { $len++; $self->{content} .= $char; $self->{modifiers}->{lc $char} = 1; $t->{line_cursor}++; } } # Handle the content parsing path for normally separated sub _fill_normal { my $self = shift; my $t = shift; # Get the content up to the next separator my $string = $self->_scan_for_unescaped_character( $t, $self->{separator} ); return undef unless defined $string; if ( ref $string ) { # End of file if ( length($$string) > 1 ) { # Complete the properties for the first section my $str = $$string; chop $str; $self->{sections}->[0] = { position => length($self->{content}), size => length($$string) - 1, type => "$self->{separator}$self->{separator}", }; $self->{_sections} = 1; } else { # No sections at all $self->{sections} = [ ]; $self->{_sections} = 0; } $self->{content} .= $$string; return 0; } # Complete the properties of the first section $self->{sections}->[0] = { position => length $self->{content}, size => length($string) - 1, type => "$self->{separator}$self->{separator}", }; $self->{content} .= $string; # We are done if there is only one section return 1 if $self->{_sections} == 1; # There are two sections. # Advance into the next section $t->{line_cursor}++; # Get the content up to the end separator $string = $self->_scan_for_unescaped_character( $t, $self->{separator} ); return undef unless defined $string; if ( ref $string ) { # End of file if ( length($$string) > 1 ) { # Complete the properties for the second section my $str = $$string; chop $str; $self->{sections}->[1] = { position => length($self->{content}), size => length($$string) - 1, type => "$self->{separator}$self->{separator}", }; } else { # No sections at all $self->{_sections} = 1; } $self->{content} .= $$string; return 0; } # Complete the properties of the second section $self->{sections}->[1] = { position => length($self->{content}), size => length($string) - 1 }; $self->{content} .= $string; 1; } # Handle content parsing for matching brace separated sub _fill_braced { my $self = shift; my $t = shift; # Get the content up to the close character my $section = $self->{sections}->[0]; my $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} ); return undef unless defined $brace_str; if ( ref $brace_str ) { # End of file if ( length($$brace_str) > 1 ) { # Complete the properties for the first section my $str = $$brace_str; chop $str; $self->{sections}->[0] = { position => length($self->{content}), size => length($$brace_str) - 1, type => $section->{type}, }; $self->{_sections} = 1; } else { # No sections at all $self->{sections} = [ ]; $self->{_sections} = 0; } $self->{content} .= $$brace_str; return 0; } # Complete the properties of the first section $section->{position} = length $self->{content}; $section->{size} = length($brace_str) - 1; $self->{content} .= $brace_str; delete $section->{_close}; # We are done if there is only one section return 1 if $self->{_sections} == 1; # There are two sections. # Is there a gap between the sections. my $char = substr( $t->{line}, ++$t->{line_cursor}, 1 ); if ( $char =~ /\s/ ) { # Go past the gap my $gap_str = $self->_scan_quote_like_operator_gap( $t ); return undef unless defined $gap_str; if ( ref $gap_str ) { # End of file $self->{content} .= $$gap_str; return 0; } $self->{content} .= $gap_str; $char = substr( $t->{line}, $t->{line_cursor}, 1 ); } $section = $sections{$char}; if ( $section ) { # It's a brace # Initialize the second section $self->{content} .= $char; $section = { %$section }; # Advance into the second section $t->{line_cursor}++; # Get the content up to the close character $brace_str = $self->_scan_for_brace_character( $t, $section->{_close} ); return undef unless defined $brace_str; if ( ref $brace_str ) { # End of file if ( length($$brace_str) > 1 ) { # Complete the properties for the second section my $str = $$brace_str; chop $str; $self->{sections}->[1] = { position => length($self->{content}), size => length($$brace_str) - 1, type => $section->{type}, }; $self->{_sections} = 2; } else { # No sections at all $self->{_sections} = 1; } $self->{content} .= $$brace_str; return 0; } else { # Complete the properties for the second section $self->{sections}->[1] = { position => length($self->{content}), size => length($brace_str) - 1, type => $section->{type}, }; $self->{content} .= $brace_str; } } elsif ( $char =~ m/ \A [^\w\s] \z /smx ) { # It is some other delimiter (weird, but possible) # Add the delimiter to the content. $self->{content} .= $char; # Advance into the next section $t->{line_cursor}++; # Get the content up to the end separator my $string = $self->_scan_for_unescaped_character( $t, $char ); return undef unless defined $string; if ( ref $string ) { # End of file if ( length($$string) > 1 ) { # Complete the properties for the second section my $str = $$string; chop $str; $self->{sections}->[1] = { position => length($self->{content}), size => length($$string) - 1, type => "$char$char", }; } else { # Only the one section $self->{_sections} = 1; } $self->{content} .= $$string; return 0; } # Complete the properties of the second section $self->{sections}->[1] = { position => length($self->{content}), size => length($string) - 1, type => "$char$char", }; $self->{content} .= $string; } else { # Error, it has to be a delimiter of some sort. # Although this will result in a REALLY illegal regexp, # we allow it anyway. # Create a null second section $self->{sections}->[1] = { position => length($self->{content}), size => 0, type => '', }; # Attach an error to the token and move on $self->{_error} = "No second section of regexp, or does not start with a balanced character"; # Roll back the cursor one char and return signalling end of regexp $t->{line_cursor}--; return 0; } 1; } ##################################################################### # Additional methods to find out about the quote # In a scalar context, get the number of sections # In an array context, get the section information sub _sections { wantarray ? @{$_[0]->{sections}} : scalar @{$_[0]->{sections}} } # Get a section's content sub _section_content { my $self = shift; my $i = shift; $self->{sections} or return; my $section = $self->{sections}->[$i] or return; return substr( $self->content, $section->{position}, $section->{size} ); } # Get the modifiers if any. # In list context, return the modifier hash. # In scalar context, clone the hash and return a reference to it. # If there are no modifiers, simply return. sub _modifiers { my $self = shift; $self->{modifiers} or return; wantarray and return %{ $self->{modifiers} }; return +{ %{ $self->{modifiers} } }; } # Get the delimiters, or at least give it a good try to get them. sub _delimiters { my $self = shift; $self->{sections} or return; my @delims; foreach my $sect ( @{ $self->{sections} } ) { if ( exists $sect->{type} ) { push @delims, $sect->{type}; } else { my $content = $self->content; push @delims, substr( $content, $sect->{position} - 1, 1 ) . substr( $content, $sect->{position} + $sect->{size}, 1 ); } } return @delims; } 1; =pod =head1 SUPPORT See the L<support section|PPI/SUPPORT> in the main module. =head1 AUTHOR Adam Kennedy E<lt>adamk@cpan.orgE<gt> =head1 COPYRIGHT Copyright 2001 - 2011 Adam Kennedy. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. The full text of the license can be found in the LICENSE file included with this module. =cut