[Templates-cvs] cvs commit: TT3/lib/Template Tag.pm

cvs@template-toolkit.org cvs@template-toolkit.org
Tue, 09 Nov 2004 13:24:01 +0000


cvs         04/11/09 13:24:01

  Modified:    lib/Template Tag.pm
  Log:
  * made what was previously known as an "open" tag the default, but changed
    it to scan ahead for an end tag if defined.
  
  Revision  Changes    Path
  1.2       +66 -96    TT3/lib/Template/Tag.pm
  
  Index: Tag.pm
  ===================================================================
  RCS file: /template-toolkit/TT3/lib/Template/Tag.pm,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Tag.pm	2004/11/08 18:46:59	1.1
  +++ Tag.pm	2004/11/09 13:24:01	1.2
  @@ -16,7 +16,7 @@
   #   modify it under the same terms as Perl itself.
   #
   # REVISION
  -#   $Id: Tag.pm,v 1.1 2004/11/08 18:46:59 abw Exp $
  +#   $Id: Tag.pm,v 1.2 2004/11/09 13:24:01 abw Exp $
   #
   #========================================================================
   
  @@ -24,19 +24,20 @@
   
   use strict;
   use warnings;
  +use Template::Parser;
   use Template::Base;
  -use vars qw( $VERSION $DEBUG $ERROR $TAG );
   use base qw( Template::Base );
   
  -$VERSION = sprintf("%d.%02d", q$Revision: 1.1 $ =~ /(\d+)\.(\d+)/);
  -$DEBUG   = 0 unless defined $DEBUG;
  -$ERROR   = '';
  -$TAG     = {
  -    start => '[%',
  -    end   => '%]',
  +our $VERSION = sprintf("%d.%02d", q$Revision: 1.2 $ =~ /(\d+)\.(\d+)/);
  +our $DEBUG   = 0 unless defined $DEBUG;
  +our $ERROR   = '';
  +our $TAG     = {
  +    start  => '[%',
  +    end    => '%]',
   };
   
   
  +
   #------------------------------------------------------------------------
   # init(\%config)
   #
  @@ -67,104 +68,75 @@
   #------------------------------------------------------------------------
   # scan($textref, $handler, $match)
   #
  -# Default scan method which looks to see if an end token is defined or 
  -# not, and then calls scan_open() or scan_closed() as appropriate.
  +# Method to scan the contents of a tag.
   #------------------------------------------------------------------------
   
   sub scan {
  -    my $self = shift;
  -    my $end;
  -
  -    $self->debug("scan()\n") if $DEBUG;
  -
  -    if (defined ($end = $self->{ end }) && length($end)) {
  -        return $self->scan_closed(@_);
  -    }
  -    else {
  -        return $self->scan_open(@_);
  -    }
  -}
  -
  -
  -#------------------------------------------------------------------------
  -# scan_open($textref, $handler, $match)
  -#
  -# Method to scan the text of an open tag that has a start token but no 
  -# pre-defined end token.
  -#------------------------------------------------------------------------
  -
  -sub scan_open {
       my ($self, $textref, $handler, $match) = @_;
  +    my $ignore = $self->{ ignore } || '';
  +    my $error;
   
  -    $self->debug("scan_open()\n") if $DEBUG;
  +    $self->debug("scan()\n") if $DEBUG;
   
       # save match info locally to make protect re-entrancy
       local $self->{ match } = $match;
  +    $match->{ lines } = 0;
  +
  +    # ignore any leading whitespace or whatever else 'ignore' is set to match
  +    $$textref =~ / \G $ignore /cogsx if $ignore;
   
  -    # call parse() method to parse forwards, scanning whatever it wants.
  -    # errors can be thrown as exceptions (hence the eval) or set via 
  -    # $self->error(), returning an undef value for $handler
       eval {
  +        # call parse() method to parse forwards through the text until it is
  +        # satisfied.  errors can be thrown as exceptions (hence the eval) or 
  +        # set via $self->error(), returning an undef value for $handler.  
           $handler = $self->parse($textref, $handler, $match);
       };
  +    $error = $@;
  +
  +    # an error may have occurred, but before reporting it back to our caller
  +    # we should first update the $match with details of the current regex match 
  +    # position so that the error generated shows the correct position in the 
  +    # source template.  if we don't get an error then we can go ahead and look
  +    # for the end token
  +
  +    if ($handler && ! $error) {
  +        my $endtag = $self->{ end };
  +        
  +        if (defined $endtag and length $endtag) {
  +            # compile regex to match end token, ignoring whitespace but checking
  +            # out for any other text that shouldn't be there
  +            my $regex  = $self->{ end_regex } ||= do {
  +                $endtag = ref $endtag eq 'Regexp' ? $endtag : quotemeta($endtag);
  +                qr/ \G $ignore (.*?) $ignore ($endtag) /sox;
  +            };
       
  +            $self->debug("scanning for end of open tag: $regex\n") if $DEBUG;
  +
  +            # scan for end token, noting error if not found
  +            if ($$textref =~ /$regex/gc) {
  +                if (defined($1) && length($1)) { 
  +                    # any text coming before end token shouldn't be there
  +                    $error = "unexpected text in tag: $1";
  +                }
  +                
  +                # save end token in $match data
  +                $match->{ end } = $2;
  +            }
  +            else {
  +                $error = "no closing tag to match $match->{ start }";
  +            }
  +        }
  +    }
  +
       # count any newlines consumed by parser between start and end positions
       my $start_pos = $match->{ offset };
       my $end_pos   = pos $$textref || 0;
       my $substr    = substr($$textref, $start_pos, $end_pos - $start_pos);
  -    $match->{ lines } = ($substr =~ tr/\n//);
  -
  -    return $@ ? $self->error($@) : $handler;
  -}
  -
  -
  -#------------------------------------------------------------------------
  -# scan_closed($textref, $handler, $match)
  -#
  -# Method to scan the text of an open tag that has both start and end 
  -# tokens defined.
  -#------------------------------------------------------------------------
  -
  -sub scan_closed {
  -    my ($self, $textref, $handler, $match) = @_;
  -
  -    $self->debug("scan_closed()\n") if $DEBUG;
  -
  -    # save match info locally to make protect re-entrancy
  -    local $self->{ match } = $match;
  -
  -    my $endtag = $self->{ end };
  -    return $self->tag_error('no end token defined for tag') 
  -        unless defined $endtag and length $endtag;
  -
  -    # compile regex to match end tag
  -    my $regex = $self->{ end_regex } ||= do {
  -        $endtag = ref $endtag eq 'Regexp' ? $endtag : quotemeta($endtag);
  -        qr/ \G (.*?) ($endtag) /sx;
  -    };
  -    
  -    $self->debug("scanning for end with $regex\n") if $DEBUG;
  -
  -    # scan for closing tag and report error if not found
  -    return $self->error("no closing tag to match $match->{ start }")
  -        unless $$textref =~ /$regex/gc;
  -
  -    # TODO: change 'size' to 'length', 'lines', etc.
  -
  -    my ($body, $end) = ($1, $2);
  -    $match->{ text  } = \$body;
  -    $match->{ end   } = $end;
  -    $match->{ lines } = 
  -          ( $match->{ start } =~ tr/\n// )
  -        + ( $body  =~ tr/\n// ) 
  -        + ( $end   =~ tr/\n// );
  -
  -    # call parse() method, catching errors thrown or reported via error()
  -    eval {
  -        $handler = $self->parse(\$body, $handler, $match);
  -    };
  +    $match->{ lines } += ($substr =~ tr/\n//);
   
  -    return $@ ? $self->error($@) : $handler;
  +    # NOTE: $handler may be undef, but we assume that an error has already
  +    # been set by a call to $self->error($msg) (e.g. in the parse() method)
  +    return $error ? $self->error($error) : $handler;
   }
   
   
  @@ -847,13 +819,11 @@
   
   =head1 VERSION
   
  -$Revision: 1.1 $
  +$Revision: 1.2 $
   
   =head1 COPYRIGHT
   
  -  Copyright (C) 1996-2004 Andy Wardley.  All Rights Reserved.
  -  Copyright (C) 1998-2002 Canon Research Centre Europe Ltd.
  -  Copyright (C) 2003-2004 Fotango Ltd.
  +Copyright (C) 1996-2004 Andy Wardley.  All Rights Reserved.
   
   This module is free software; you can redistribute it and/or
   modify it under the same terms as Perl itself.
  @@ -861,10 +831,10 @@
   =head1 SEE ALSO
   
   For examples of tag subclasses that perform more specific processing,
  -see L<Template::TT3::Tag::Comment>, L<Template::TT3::Tag::Escape>,
  -L<Template::TT3::Tag::Variable>, and L<Template::TT3::Tag::Directive>.
  -For more information about the scanner and document classes, see
  -L<Template::TT3::Scanner> and L<Template::TT3::Document> respectively.
  +see L<Template::Tag::Comment>, L<Template::Tag::Escape>,
  +L<Template::Tag::Variable>, and L<Template::Tag::Directive>.
  +For more information about the scanner and handler classes, see
  +L<Template::Scanner> and L<Template::Handler> respectively.
   
   =cut