[Templates-cvs] cvs commit: TT3/examples compiler.pl custom.pl htmlt.pl scanner.pl tt3.pl html2.pl

cvs@template-toolkit.org cvs@template-toolkit.org
Sat, 11 Dec 2004 13:56:54 +0000


cvs         04/12/11 13:56:54

  Modified:    examples compiler.pl custom.pl htmlt.pl scanner.pl tt3.pl
  Removed:     examples html2.pl
  Log:
  * updated examples
  
  Revision  Changes    Path
  1.2       +49 -16    TT3/examples/compiler.pl
  
  Index: compiler.pl
  ===================================================================
  RCS file: /template-toolkit/TT3/examples/compiler.pl,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- compiler.pl	2004/01/30 09:32:52	1.1
  +++ compiler.pl	2004/12/11 13:56:54	1.2
  @@ -1,6 +1,6 @@
   #!/usr/bin/perl -w                                            # -*- perl -*-
   #
  -# Perl script written by Andy Wardley.  This is free software.
  +# This example shows the Perl code generated by the compiler.
   #
   
   use strict;
  @@ -9,15 +9,13 @@
   use Template::TT3::Compiler;
   
   my $compiler = Template::TT3::Compiler->new({
  -    grammar     => 'Template::TT3::Grammar::TT3',
  -    tagset      => 'Template::TT3::Tagset::TT3',
  -    generator   => 'Template::TT3::Generator::Debug',
       interpolate => 1,
  +    pretty => 1,
   }) || die Template::TT3::Compiler->error();
   
   local $/ = undef;
   my $input = <DATA>;
  -my $output = $compiler->compile($input)
  +my $output = $compiler->compile($input, name => 'compiler example')
       || die $compiler->error();
   
   print "-- input --\n",
  @@ -26,14 +24,49 @@
         $output, "\n";
   
   __DATA__
  -Hello World
  -[% foo %] and $bar and ${baz} but not \$bam
  -[% foo('hello world').bar('nice to see you' ) %]
  -you can now have spaces in embedded variables
  -like $foo('hello world').bar('and it works') as expected
  -and also you can do $foo(bar + baz)
  -[% INCLUDE header
  -      title = 'Hello World'
  -      author = page.author or site.author
  -%]
  -The end
  +[% META title = 'Compiler Example' %]
  +
  +Hello World!
  +
  +Here are some variables:
  +    [% foo %] and $bar and ${baz} but not \$bam
  +    [% foo('hello world').bar('nice to see you') %]
  +
  +INTERPOLATE is currently set ON, but we can turn it off like so: 
  +[% INTERPOLATE off %] and then the embedded variables below are not
  +interpolated:
  +
  +    [% foo %] but neither $bar nor ${baz}
  +
  +There's a new MY variable type.  Here's an example:
  +
  +    [% MY x = 'private bar variable' %]
  +    [% y = 'regular foo variable' %]
  +
  +The 'x' variable is declared as MY, so it is created in the underlying
  +Perl code as a "my $var_x" variable, making it fast to access, and
  +totally private to the current lexical template scope.  The 'y'
  +variable is a regular TT variable that is stored in the current
  +context variables.
  +
  +    [% x %]
  +    [% y %]
  +
  +The parser and scanner are now smart enough to Do The Right Thing when
  +the tag start or end tokens are embedded in a quote string.
  +
  +    [% start_tag = '[%'   # no problem
  +       end_tag   = '%]'  
  +    %]
  +    
  +You can now also included spaces in embedded variables, for
  +example in argument lists:  [% INTERPOLATE on %]
  +
  +    $foo('hello world').bar(10, 20, 30)
  +
  +You can also put full expressions in argument lists:
  +
  +    [% foo(x * 20) %]
  +    [% bar(y && z ? x * 10 : x * 20) %]
  +
  +And much, much more...
  
  
  
  1.4       +2 -4      TT3/examples/custom.pl
  
  Index: custom.pl
  ===================================================================
  RCS file: /template-toolkit/TT3/examples/custom.pl,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- custom.pl	2004/01/30 14:35:46	1.3
  +++ custom.pl	2004/12/11 13:56:54	1.4
  @@ -7,15 +7,13 @@
   use warnings;
   use lib qw( ../lib ./lib );
   use Template::TT3::Compiler;
  +use Template::Generator::Debug;
   
   my $compiler = Template::TT3::Compiler->new({
  -    grammar     => 'Template::TT3::Grammar::TT3',
  -    tagset      => 'Template::TT3::Tagset::TT3',
  -    generator   => 'Template::TT3::Generator::Debug',
  +    generator   => 'Template::Generator::Debug',
       interpolate => 1,
       directives  => {
           hello => { 
  -            rule => 'expression' ,
           },
       },
       generators => {
  
  
  
  1.4       +160 -84   TT3/examples/htmlt.pl
  
  Index: htmlt.pl
  ===================================================================
  RCS file: /template-toolkit/TT3/examples/htmlt.pl,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- htmlt.pl	2004/11/26 12:50:54	1.3
  +++ htmlt.pl	2004/12/11 13:56:54	1.4
  @@ -1,130 +1,206 @@
   #!/usr/bin/perl -w                                            # -*- perl -*-
   #
  -# Example simulating HTML::Template
  +# This example is a quick proof-of-concept hack to show how you
  +# can write a custom tag to parse your own language (e.g. HTML::Template)
  +# and feed structured expression events (currently a little clumsy) into
  +# the Template::Handler back-end, and leave TT to handle the rest.  Templates
  +# are compiled into Perl code, just as for regular TT templates with which
  +# they are fully interoperable.
   # 
  -# Perl script written by Andy Wardley.  This is free software.
  +# Andy Wardley.  11th December 2004.
   #
  -# <TMPL_VAR>
  -# <TMP_INCLUDE>
  -# <TMPL_IF> ... <TMPL_ELSE> ... </TMPL_IF>
  -# <TMPL_UNLESS> ... <TMPL_ELSE> ... </TMPL_UNLESS>
  -# <TMPL_LOOP> ... </TMPL_LOOP>
  +# Directives implemented:
  +#   <TMPL_VAR>
  +#   <TMP_INCLUDE>
  +#   <TMPL_IF> ... <TMPL_ELSE> ... </TMPL_IF>
  +#   <TMPL_UNLESS> ... <TMPL_ELSE> ... </TMPL_UNLESS>
  +#
  +# Not yet implemented:
  +#   <TMPL_LOOP> ... </TMPL_LOOP>   # not implemented in generator
  +#
   
   use strict;
   use warnings;
   use lib qw( ../lib ./lib );
  -use Template::TT3::Compiler;
  -use Template::TT3::Tag::Closed;
  -
  -package Template::TT3::Tag::HTMLTemplate;
  -use base qw( Template::TT3::Tag::Closed );
  -use vars qw( $TAG );
  +use Template;
  +use Template::Compiler;
  +use Template::Parser;
  +use Template::Tag::Closed;
  +use Template::Generator::Debug;
  +use Template::Directive::Include;
  +
  +# run with '-d' option to enable debugging
  +my $DEBUG = 
  +#$Template::Parser::DEBUG =
  +grep(/^--?d(ebug)?/, @ARGV);
  +
  +#------------------------------------------------------------------------
  +# define a custom Tag object for parsing HTML::Template-like tags.
  +#------------------------------------------------------------------------
  +
  +package Template::Tag::HTMLTemplate;
  +use Template::Parser;
  +use base qw( Template::Tag::Closed );
   
  -$TAG = {
  -    name  => 'htmltemplate',
  +our $TAG = {
       start => qr[</?TMPL_\w+]i,
       end   => qr[/?>],
   };
   
  +our $PARSER = Template::Parser->new( tag_end => $TAG->{ end } );
  +
  +
   sub parse {
       my ($self, $textref, $handler, $match) = @_;
       my $start = $match->{ start };
  -    
  +    my $name;
  +
  +    $handler->line($match->{ line });
  +
       # remove leading '<' and TMPL
       $start =~ s/^<//;
       $start =~ s/TMPL_//i;
   
  -    # copy all arguments into hash
  -    my $args = {  };
  -    while ($$textref =~ /^\s*(\w+)="([^"]*)"/g) {
  -        $args->{ lc $1 } = $2;
  +    # many directives start with NAME="something", so we'll look
  +    # for that
  +    if ($$textref =~ s/ \s* NAME = "([^"]*)" //x) {
  +        $name = $1;
       }
  -    
  -    my $dir;
  +
       if ($start eq 'INCLUDE') {
  +
  +        #----------------------------------------------------------------
  +        # <TMPL_INCLUDE NAME="header" arg1="value" arg="value2">
  +        #----------------------------------------------------------------
  +
           return $self->error("no NAME provided for TMPL_INCLUDE directive")
  -            unless $args->{ name };
  -        return $handler->directive( include => $args->{ name } )
  +            unless $name;
  +
  +        my $paths = $PARSER->parse_paths(\$name)
  +            || return $self->error("invalid NAME in TMPL_INCLUDE directive");
  +
  +        my $params = $PARSER->parse_params($textref);
  +
  +        return $handler->expr( include => $paths, $params )
               || $self->error($handler->error());
       }
       elsif ($start eq 'VAR') {
  +
  +        #----------------------------------------------------------------
  +        # <TMPL_VAR NAME="foobar">
  +        #----------------------------------------------------------------
  +
           return $self->error("no NAME provided for TMPL_VAR directive")
  -            unless $args->{ name };
  -        return $handler->directive( get => $args->{ name } )
  +            unless $name;
  +
  +        my $expr = $PARSER->parse_variable(\$name) 
  +            || return $self->error($PARSER->error());
  +
  +        return $handler->expr( get => $expr )
               || $self->error($handler->error());
       }
  -    elsif ($start eq 'LOOP') {
  -        return $self->error("no NAME provided for TMPL_LOOP directive")
  -            unless $args->{ name };
  -        return $handler->start_block( loop => $args->{ name } )
  -            ? $handler : $self->error($handler->error());
  -    }
  -    elsif ($start eq '/LOOP') {
  -        return $handler->end_block('loop')
  -            ? $handler : $self->error($handler->error());
  -    }
       elsif ($start eq 'IF') {
  +
  +        #----------------------------------------------------------------
  +        # <TMPL_IF NAME="foobar"> ... </IF>
  +        #----------------------------------------------------------------
  +
           return $self->error("no NAME provided for TMPL_IF directive")
  -            unless $args->{ name };
  -        # tmp hack
  -        return $handler->start_block( if => $args->{ name } )
  -            ? $handler : $self->error($handler->error());
  +            unless $name;
  +
  +        my $expr = $PARSER->parse_expression(\$name) 
  +            || return $self->error($PARSER->error());
  +
  +        return $handler->start_expr( [ if => $expr ],
  +                                     { keyword => $start, 
  +                                       line    => $match->{ line },
  +                                     } )
  +            || $self->error($handler->error());
       }
       elsif ($start eq 'UNLESS') {
  +
  +        #----------------------------------------------------------------
  +        # <TMPL_UNLESS NAME="foobar"> ... </UNLESS>
  +        #----------------------------------------------------------------
  +
           return $self->error("no NAME provided for TMPL_UNLESS directive")
  -            unless $args->{ name };
  -        return $handler->start_block( unless => $args->{ name } )
  -            ? $handler : $self->error($handler->error());
  +            unless $name;
  +
  +        my $expr = $PARSER->parse_expression(\$name) 
  +            || return $self->error($PARSER->error());
  +
  +        return $handler->start_expr( [ unless => $expr ],
  +                                     { keyword => $start, 
  +                                       line    => $match->{ line },
  +                                     } )
  +            || $self->error($handler->error());
       }
       elsif ($start eq 'ELSE') {
  -        return $handler->next_block('else')
  -            ? $handler : $self->error($handler->error());
  +        return $handler->next_expr({ keyword => 'ELSE', 
  +                                     line => $match->{ line } 
  +                                   })
  +            || $self->error($handler->error());
       }
       elsif ($start eq '/IF') {
  -        # tmp hack
  -        return $handler->end_block()
  -            ? $handler : $self->error($handler->error());
  +        return $handler->end_expr()
  +            || $self->error($handler->error());
       }
  +    elsif ($start eq 'LOOP') {
  +        die "LOOP not yet implemented\n";
  +    }
  +    elsif ($start eq '/LOOP') {
  +        die "/LOOP not yet implemented\n";
  +    }
       else {
           return $self->error("unknown directive: $start");
       }
   }
   
  +
  +#------------------------------------------------------------------------
  +# create a custom compiler with this tag, and only this tag defined.
  +# (we could subclass from Template::TT3::Compiler to get all the regular
  +# TT tags in addition to our custom HTML::Template tag).
  +#------------------------------------------------------------------------
  +
   package main;
  +
  +my $compiler = Template::Compiler->new({
  +    tags => [ htmlt => Template::Tag::HTMLTemplate->new() ],
  +}) || die Template::Compiler->error();
  +
  +
  +#------------------------------------------------------------------------
  +# create a Template object with the custom compiler enabled by default,
  +# and have it process our example template.
  +#------------------------------------------------------------------------
  +
  +my $tt3 = Template->new({
  +    template_path => 'templates',
  +    compilers => {
  +        default => $compiler,
  +    },
  +}) || die Template->error();
  +
  +if ($DEBUG) {
  +    my $template = $tt3->context->template('htmltemplate')
  +        || die $tt3->error();
  +    print "template code: ", $template->source->code(), "\n";
  +}
  +
  +$tt3->process('htmltemplate', { 
  +    message     => 'Hello World',
  +    interesting => 'very',
  +    myhash => {
  +        x => 100,
  +        y => {
  +            z => 200,
  +        },
  +    },
  +    mycode => sub {
  +        return "called subref(" . join(', ', @_) . ")";
  +    },
  +}) || die $tt3->error();
  +
  +
   
  -my $compiler = Template::TT3::Compiler->new({
  -    tags => [ Template::TT3::Tag::HTMLTemplate->new() ],
  -    generator   => 'Template::TT3::Generator::Debug',
  -}) || die Template::TT3::Compiler->error();
  -
  -local $/ = undef;
  -my $input = <DATA>;
  -my $output = $compiler->compile($input)
  -    || die $compiler->error();
  -
  -print "-- input --\n",
  -      $input, "\n",
  -      "-- output --\n",
  -      $output, "\n";
  -
  -__DATA__
  -<html>
  -  <head>
  -    <title>Template Toolkit / HTML::Template Test</title>
  -  </head>
  -  <body>
  -    <TMPL_INCLUDE NAME="header">
  -
  -    <p>
  -      my name is <TMPL_VAR NAME="myname">.
  -    </p>
  -    <TMPL_IF NAME="interesting">
  -      I am very interesting.
  -    <TMPL_LOOP NAME="interest">
  -      I like <TMPL_VAR NAME="interest"/>
  -    </TMPL_LOOP>
  -    <TMPL_ELSE>
  -      I am very boring.
  -    </TMPL_IF>
  -  </body>
  -</html>
  
  
  
  1.2       +49 -35    TT3/examples/scanner.pl
  
  Index: scanner.pl
  ===================================================================
  RCS file: /template-toolkit/TT3/examples/scanner.pl,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- scanner.pl	2004/02/02 10:03:52	1.1
  +++ scanner.pl	2004/12/11 13:56:54	1.2
  @@ -1,30 +1,46 @@
   #!/usr/bin/perl -w                                            # -*- perl -*-
   #
  -# Perl script written by Andy Wardley.  This is free software.
  +# This example shows how the scanner can be customised to detect
  +# different kinds of embedded tags.  We create a Template::Compiler
  +# and define a set of tags for the Template::Scanner to detect.
  +# We provide custom scanning subroutines to parse the contents 
  +# of the tag and generate parse events against the handler.
  +# These are later converted into a text representation of the 
  +# parse tree for debugging purposes, thanks to the efforts of
  +# Template::Generator::Debug.  We additionally define two generator
  +# subroutines to help the generator turn our compiled directives
  +# into real-world code.  This would normally be Perl, but it's 
  +# just some debugging output in this example.
   #
  +# Andy Wardley.  11th December 2004.
  +#
  +
   
   use strict;
   use warnings;
   use lib qw( ../lib ./lib );
  -use Template::TT3::Handler;
  -use Template::TT3::Scanner;
  -use Template::TT3::Generator::Debug;
  -use Template::TT3::Tag;
  +use Template::Compiler;
  +use Template::Generator::Debug;
  +use Template::Tag::Closed;
   
  -# define scanner
  -my $scanner = Template::TT3::Scanner->new({
  +my $compiler = Template::Compiler->new({
       tags => [
  -        Template::TT3::Tag->new({   # TODO: should be able to pass hash refs
  -            start => '<$',          #       and have the scanner auto-vivify
  -            end   => '$>',          #       Tag objects for you
  +        dollar => Template::Tag::Closed->new({ 
  +            start => '<$',         
  +            end   => '$>', 
               parse => \&dollar_tag,
           }),
  -        Template::TT3::Tag->new({
  +        question => Template::Tag::Closed->new({
               start => '<?',
               end   => '?>',
               parse => \&question_tag,
           }),
  -    ]
  +    ],
  +    generator  => Template::Generator::Debug->new(),
  +    generators => {
  +        question => \&generate_question,
  +        dollar   => \&generate_dollar,
  +    },
   });
   
   # subroutine for parsing <$ ... $> tags
  @@ -38,7 +54,7 @@
       my $locn = $self->location();
   
       # notify the handler of some new content
  -    return $handler->directive( dollar => "at $locn: [$$textref]" )
  +    return $handler->expr( dollar => "at $locn: [$$textref]" )
           || $self->error($handler->error());
   }
   
  @@ -51,29 +67,28 @@
       my $locn = $self->location();
   
       # first argument denotes the content type, any other args can follow
  -    return $handler->directive( question => "at $locn: [$$textref]" )
  +    return $handler->expr( question => "at $locn: [$$textref]" )
           || $self->error($handler->error());
   }
   
  +# subroutine for generating code for question tag
  +sub generate_question {
  +    my ($self, $text) = @_;
  +    return "QUESTION $text";
  +}
  +
  +# subroutine for generating code for dollar tag
  +sub generate_dollar {
  +    my ($self, $text) = @_;
  +    return "DOLLAR $text";
  +}
  +
   # read input from __DATA__
   local $/ = undef;
   my $input = <DATA>;
   
  -# define a handler to construct document content
  -my $handler = Template::TT3::Handler->new();
  +my $output = $compiler->compile($input) || die $compiler->error();
   
  -# scan document
  -$handler->start();
  -$scanner->scan($input, $handler) || die $scanner->error();
  -my $result = $handler->end() || die $handler->error();
  -
  -# create a generator to display document
  -my $generator = Template::TT3::Generator::Debug->new({});
  -#    generators );
  -
  -my $output = $generator->generate($result)
  -    || die $generator->error();
  -
   print "-- input --\n",
         $input, "\n",
         "-- output --\n",
  @@ -89,11 +104,10 @@
       lines
       and ?> the scanner takes
   care of counting 
  -<$
  -
  -    line
  -    numbers
  -
  -$> for you
  -
  +all the 
  +<$ 
  +    line 
  +    numbers 
  +$>
  +for you
   
  
  
  
  1.2       +10 -31    TT3/examples/tt3.pl
  
  Index: tt3.pl
  ===================================================================
  RCS file: /template-toolkit/TT3/examples/tt3.pl,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- tt3.pl	2004/11/26 12:51:49	1.1
  +++ tt3.pl	2004/12/11 13:56:54	1.2
  @@ -1,40 +1,19 @@
   #!/usr/bin/perl -w                                            # -*- perl -*-
   #
  -# Perl script written by Andy Wardley.  This is free software.
  +# example/tt3.pl
   #
  +# The first TT3 script ever to process a complete template
  +# This is free software.  May a thousand flowers blossom.
  +#
  +# Andy Wardley.  11th December 2004
  +#
   
   use strict;
   use warnings;
   use lib qw( ../lib ./lib );
  -use Template::TT3::Compiler::TT3;
  -
  -my $compiler = Template::TT3::Compiler::TT3->new({
  -    generator   => 'Template::TT3::Generator::Debug',
  -    interpolate => 1,
  -    directives  => {
  -        hello => { 
  -            rule => 'expression' ,
  -        },
  -    },
  -    generators => {
  -        hello => sub {
  -            my ($self, $expr) = @_;
  -            $expr = $self->generate($expr) || return;
  -            $expr =~ s/\n/\n  /g;
  -            return "<hello:\n  $expr\n>";
  -        },
  -    },
  -}) || die Template::TT3::Compiler->error();
  -
  -local $/ = undef;
  -my $input = <DATA>;
  -my $output = $compiler->compile($input)
  -    || die $compiler->error();
  +use Template;
   
  -print "-- input --\n",
  -      $input, "\n",
  -      "-- output --\n",
  -      $output, "\n";
  +my $tt3 = Template->new( template_path => 'templates' );
   
  -__DATA__
  -[% HELLO world + his.wife %]
  +$tt3->process("example.html", { message => 'Hello World' })
  +    || die $tt3->error();