From 95bc376840b6c2f317386e6edb53055d5a78409d Mon Sep 17 00:00:00 2001 From: Andy Wardley Date: Sat, 19 Dec 2009 22:00:34 +0000 Subject: [PATCH] Split the Template::TT3::Elements libraries into separate files --- docs/pod/Template/TT3/Status.pod | 54 ++- docs/pod/Template/TT3/TODO.pod | 6 + lib/Template/TT3/Class.pm | 20 +- lib/Template/TT3/Class/Element.pm | 253 ++++++++++++++ .../{Factory/Class.pm => Class/Factory.pm} | 8 +- lib/Template/TT3/Constants.pm | 2 - lib/Template/TT3/Dialects.pm | 5 +- lib/Template/TT3/Element/Command/Fill.pm | 9 +- lib/Template/TT3/Element/Comment.pm | 11 + lib/Template/TT3/Element/Delimiter.pm | 77 +++++ lib/Template/TT3/Element/Dquote.pm | 50 +++ lib/Template/TT3/Element/End.pm | 10 + lib/Template/TT3/Element/Eof.pm | 16 + lib/Template/TT3/Element/Fragment.pm | 16 + lib/Template/TT3/Element/Literal.pm | 66 ++-- lib/Template/TT3/Element/Number.pm | 28 ++ .../{Elements => Element/Operator}/Boolean.pm | 35 +- .../{Elements => Element/Operator}/Number.pm | 241 +++++--------- lib/Template/TT3/Element/Operator/Range.pm | 29 ++ lib/Template/TT3/Element/Operator/Text.pm | 103 ++++++ lib/Template/TT3/Element/Padding.pm | 24 ++ lib/Template/TT3/Element/Role/Expr/Name.pm | 33 ++ lib/Template/TT3/Element/Role/Filename.pm | 19 +- lib/Template/TT3/Element/Role/NameExpr.pm | 3 + lib/Template/TT3/Element/Separator.pm | 28 ++ lib/Template/TT3/Element/Squote.pm | 10 + lib/Template/TT3/Element/String.pm | 57 ++++ lib/Template/TT3/Element/TagEnd.pm | 9 + lib/Template/TT3/Element/TagStart.pm | 30 ++ lib/Template/TT3/Element/Terminator.pm | 15 + lib/Template/TT3/Element/Text.pm | 12 + lib/Template/TT3/Element/Whitespace.pm | 41 +++ lib/Template/TT3/Elements.pm | 12 +- lib/Template/TT3/Elements/Core.pm | 13 +- lib/Template/TT3/Elements/Punctuation.pm | 207 ------------ lib/Template/TT3/Elements/Structure.pm | 135 -------- lib/Template/TT3/Elements/Text.pm | 314 ------------------ lib/Template/TT3/Elements/Whitespace.pm | 146 -------- lib/Template/TT3/Engines.pm | 10 +- lib/Template/TT3/Exception/Syntax.pm | 1 + lib/Template/TT3/Exceptions.pm | 122 ++++++- lib/Template/TT3/Factory.pm | 7 +- lib/Template/TT3/Grammar/TT3.pm | 8 +- lib/Template/TT3/Modules.pm | 51 +-- lib/Template/TT3/Providers.pm | 7 +- lib/Template/TT3/Tag/Control.pm | 1 - lib/Template/TT3/Types.pm | 60 +--- t/class/element.t | 45 +++ t/class/factory.t | 45 +++ t/dialect/dialects.t | 73 ++-- t/element/literal.t | 42 +++ t/element/text.t | 42 +++ t/factory/dialects.t | 45 +++ t/{engine => factory}/engines.t | 11 +- t/factory/exceptions.t | 35 ++ t/{provider => factory}/providers.t | 8 +- t/{modules => factory}/types.t | 32 +- t/modules/dialects.t | 69 ---- 58 files changed, 1595 insertions(+), 1266 deletions(-) create mode 100644 lib/Template/TT3/Class/Element.pm rename lib/Template/TT3/{Factory/Class.pm => Class/Factory.pm} (91%) create mode 100644 lib/Template/TT3/Element/Comment.pm create mode 100644 lib/Template/TT3/Element/Delimiter.pm create mode 100644 lib/Template/TT3/Element/Dquote.pm create mode 100644 lib/Template/TT3/Element/End.pm create mode 100644 lib/Template/TT3/Element/Eof.pm create mode 100644 lib/Template/TT3/Element/Fragment.pm create mode 100644 lib/Template/TT3/Element/Number.pm rename lib/Template/TT3/{Elements => Element/Operator}/Boolean.pm (66%) rename lib/Template/TT3/{Elements => Element/Operator}/Number.pm (53%) create mode 100644 lib/Template/TT3/Element/Operator/Range.pm create mode 100644 lib/Template/TT3/Element/Operator/Text.pm create mode 100644 lib/Template/TT3/Element/Padding.pm create mode 100644 lib/Template/TT3/Element/Role/Expr/Name.pm create mode 100644 lib/Template/TT3/Element/Separator.pm create mode 100644 lib/Template/TT3/Element/Squote.pm create mode 100644 lib/Template/TT3/Element/String.pm create mode 100644 lib/Template/TT3/Element/TagEnd.pm create mode 100644 lib/Template/TT3/Element/TagStart.pm create mode 100644 lib/Template/TT3/Element/Terminator.pm create mode 100644 lib/Template/TT3/Element/Text.pm create mode 100644 lib/Template/TT3/Element/Whitespace.pm delete mode 100644 lib/Template/TT3/Elements/Punctuation.pm delete mode 100644 lib/Template/TT3/Elements/Structure.pm delete mode 100644 lib/Template/TT3/Elements/Text.pm delete mode 100644 lib/Template/TT3/Elements/Whitespace.pm create mode 100644 t/class/element.t create mode 100644 t/class/factory.t create mode 100644 t/element/literal.t create mode 100644 t/element/text.t create mode 100644 t/factory/dialects.t rename t/{engine => factory}/engines.t (87%) create mode 100644 t/factory/exceptions.t rename t/{provider => factory}/providers.t (88%) rename t/{modules => factory}/types.t (87%) delete mode 100644 t/modules/dialects.t diff --git a/docs/pod/Template/TT3/Status.pod b/docs/pod/Template/TT3/Status.pod index 4dc2c07..ef234b1 100644 --- a/docs/pod/Template/TT3/Status.pod +++ b/docs/pod/Template/TT3/Status.pod @@ -27,6 +27,10 @@ This is my immediate stack as of Saturday 19th December. * T~Element::Text # subclass of literal (extract from T~Es~Text) * ...then other elements currently in the T~Elements::* modules +Sidetracked onto cleaning up factories: + + * Template::TT3::Types + =head1 ELEMENT MODULES Elements are the basic nodes that comprise a parsed template tree. They're @@ -53,14 +57,14 @@ These are the current element library modules. This list should eventually dwindle to nothing as they are split up and replaced by individual element modules. - [ ] Template::TT3::Element::Boolean - [ ] Template::TT3::Element::Core - [ ] Template::TT3::Element::Number - [ ] Template::TT3::Element::Operator - [ ] Template::TT3::Element::Punctuation - [ ] Template::TT3::Element::Structure - [ ] Template::TT3::Element::Text - [ ] Template::TT3::Element::Whitespace + [ ] Template::TT3::Elements::Boolean + [ ] Template::TT3::Elements::Core + [ ] Template::TT3::Elements::Number ** WORKING ON THIS ** + [ ] Template::TT3::Elements::Operator + [ ] Template::TT3::Elements::Punctuation + [ ] Template::TT3::Elements::Structure + [ ] Template::TT3::Elements::Text ** WORKING ON THIS ** + [ ] Template::TT3::Elements::Whitespace =head2 Core Elements @@ -72,6 +76,15 @@ modules. [ ] Template::TT3::Element::Text [ ] Template::TT3::Element::Word +Newly added (split out from Template::TT3::Elements::XXX libraries. + + [ ] Template::TT3::Element::String; + [ ] Template::TT3::Element::Squote; + [ ] Template::TT3::Element::Dquote; + [ ] Template::TT3::Element::Padding; + [ ] Template::TT3::Element::TagStart; + [ ] Template::TT3::Element::Whitespace; + =head2 Variable Elements These elements are used to represent variables and operations relating to @@ -102,8 +115,10 @@ These elements implement various operators of the language. [ ] Template::TT3::Element::Operator::Assign [ ] Template::TT3::Element::Operator::Dot + [ ] Template::TT3::Element::Operator::Number ** WORKING ON THIS ** [ ] Template::TT3::Element::Operator::Pair [ ] Template::TT3::Element::Operator::Ternary + [ ] Template::TT3::Element::Operator::Text ** WORKING ON THIS ** =head2 Sigils @@ -205,6 +220,24 @@ at them. [ ] Template::TT3::Element::HTML::Table [ ] Template::TT3::Element::HTML +=head1 Factory Modules + +Factory modules are responsible for loading modules and instantiating objects. +I'm in the process of migrating Template::TT3::Factory::Class to T~Class::Factory. + + [X] Template::TT3::Factory # base class + [X] Template::TT3::Class::Factory # metaprogramming class + + [X] Template::TT3::Engines + [X] Template::TT3::Exceptions + [X] Template::TT3::Dialects + [X] Template::TT3::Providers + + [ ] Template::TT3::Types + - remove pre-defined types and have T~Variables manage the list + instead + + =head1 Other Modules Until I get a chance to properly organise a list of the other modules, I'll @@ -213,11 +246,6 @@ just dump everything else here. The following modules are believed to be complete, clean, tested and documented. - [X] Template::TT3::Factory - [X] Template::TT3::Factory::Class - [X] Template::TT3::Engines - [X] Template::TT3::Dialects - [X] Template::TT3::Providers =head1 AUTHOR diff --git a/docs/pod/Template/TT3/TODO.pod b/docs/pod/Template/TT3/TODO.pod index ad51499..0b677b2 100644 --- a/docs/pod/Template/TT3/TODO.pod +++ b/docs/pod/Template/TT3/TODO.pod @@ -227,6 +227,12 @@ Probably best to cleave it back into 3 parts like it (well, its predecessor) used to be. +=head1 Class Modules + +These should all be moved to sit under the C namespace. +e.g. C should be +C. + =head1 Configuration =head2 Amalgamating Arguments diff --git a/lib/Template/TT3/Class.pm b/lib/Template/TT3/Class.pm index dcafba6..d7cd7eb 100644 --- a/lib/Template/TT3/Class.pm +++ b/lib/Template/TT3/Class.pm @@ -59,6 +59,8 @@ sub view { } +# TODO: this is being deprecated in favour of dedicated +# Template::TT3::Class::Element subclass sub as { my ($self, $roles) = @_; my $base = $self->AS_ROLE; @@ -146,7 +148,7 @@ sub generate { } -sub subclass { +sub OLD_subclass { my $self = shift; my $base = $self->{ name }; my $classes = @_ == 1 ? shift : [ @_ ]; @@ -188,6 +190,12 @@ sub subclass { } +sub subclass { + my ($self, $name) = @_; + class( $self->{ name }.PKG.camel_case($name), ref $self ); +} + + # TODO: change this to generate_elements() because it's no longer specific # to operators @@ -285,7 +293,7 @@ sub generate_pre_post_ops { } -sub generate_number_ops { +sub OLD_generate_number_ops { shift->generate_ops( { id => 'num', methods => 'text number value values' }, @_ @@ -293,7 +301,7 @@ sub generate_number_ops { } -sub generate_number_assign_ops { +sub OLD_generate_number_assign_ops { shift->generate_ops( { id => 'num', methods => 'number value values' }, @_ @@ -301,7 +309,7 @@ sub generate_number_assign_ops { } -sub generate_text_ops { +sub OLD_generate_text_ops { shift->generate_ops( { id => 'txt', methods => 'text value values' }, @_ @@ -309,7 +317,7 @@ sub generate_text_ops { } -sub generate_text_assign_ops { +sub OLD_generate_text_assign_ops { shift->generate_ops( { id => 'txt', methods => 'value values' }, @_ @@ -317,7 +325,7 @@ sub generate_text_assign_ops { } -sub generate_boolean_ops { +sub OLD_generate_boolean_ops { shift->generate_ops( { id => 'bool', methods => 'value values' }, @_ diff --git a/lib/Template/TT3/Class/Element.pm b/lib/Template/TT3/Class/Element.pm new file mode 100644 index 0000000..fc7189a --- /dev/null +++ b/lib/Template/TT3/Class/Element.pm @@ -0,0 +1,253 @@ +package Template::TT3::Class::Element; + +use Carp; +use Template::TT3::Class + version => 0.01, + debug => 0, + uber => 'Template::TT3::Class', + modules => 'ELEMENT_MODULE ELEMENT_ROLE_MODULE', + constants => 'ARRAY HASH CODE DELIMITER PKG DOT BLANK', + utils => 'camel_case', + hooks => { + type => \&type, + roles => \&roles, + parse_expr => \&parse_expr, + }, + constant => { + BASE_OP => 'Template::TT3::Element::Operator', + }; + + +our $DEBUG_OPS = 0; + +# We have to do these after the above so that class() is property defined. + +CLASS->export_before( + sub { + # Create a Template::TT3::Class::Element metaclass object as a wrapper + # around the target class and then call its element() method to + # prepare the class as an element subclass + my ($class, $target) = @_; + return if $target eq 'Badger::Class'; + class($target, $class) + ->constants(':elements'); + } +); + +CLASS->export_after( + sub { + my ($class, $target) = @_; + return if $target eq 'Badger::Class'; + class($target, $class) + ->type + ->base( $class->ELEMENT_MODULE ); + }, +); + + +sub type { + my ($self, $type) = @_; + + # We may have been called explicitly with a custom 'type => XXX' hook. + # In which case, the implicit type() called by "after export" action will + # create a duplicate. So we return silently if a type() method is + # already defined. Otherwise we create it as a constant subroutine. + return $self + if $self->code_ref('type'); + + unless ($type) { + $type = $self->{ name }; + $type = $1 if $type =~ /Element::(.*)$/; + $type = lc $type; + $type =~ s/\W+/_/g; + } + + $self->debug("setting type for $self to $type") if DEBUG; + + $self->method( type => sub() { $type } ); + + return $self; +} + + +sub roles { + my ($self, $roles) = @_; + my $base = $self->ELEMENT_ROLE_MODULE; + + $roles = [ split(DELIMITER, $roles) ] + unless ref $roles eq ARRAY; + + $self->mixin( + map { + join( + PKG, $base, map { camel_case($_) } split(/\./, $_) + ) + } + @$roles + ); +} + +sub type_role { + shift->roles( join(DOT, @_ ) ); +} + +sub parse_expr { + shift->type_role( expr => @_ ); +} + +sub generate_elements { + my $self = shift; + my $spec = shift; + my $args = @_ == 1 && ref $_[0] ? shift : [ @_ ]; + + # methods gives us a list of method names that we want to alias to + my $methods = $spec->{ methods } || 'value'; + $methods = [ split(DELIMITER, $methods) ] + unless ref $methods eq ARRAY; + + $args = + ref $args eq ARRAY ? [ @$args ] # a copy we can mutate + : ref $args eq HASH ? [ %$args ] # flatten hash to list + : die "Invalid arguments provided to generate_ops() : $args"; + + while (@$args) { + # First item is name, then any mixin/base classes, followed by a + # CODE reference which should be installed as the value() method, + # with an alias of values() pointing at the same subroutine. + my ($name, @bases, $base, $code); + $name = shift @$args; + $name = $self->{ name }.PKG.camel_case($name); + + # TODO: change these bases to mixin roles + while (@$args && ! ref $args->[0]) { + $base = shift @$args; + push( + @bases, + $base =~ /::/ + ? $base + : BASE_OP.PKG.camel_case($base) + ); + } + + push(@bases, $self->{ name }); + + die "No subroutine specified for $name in generate_ops()" + unless @$args && ($code = shift @$args); + + die "Invalid subroutine specified for $name in generate_ops(); $code" + unless ref $code eq CODE; + + if ($DEBUG_OPS) { + # generate wrapper that calls debug_ops() before the real sub + my $real = $code; + $code = sub { + $_[0]->debug_op($_[1]); + $real->(@_); + }; + }; + +# print "++ $name\n"; + + class->export( + $name => [ + base => \@bases, + methods => { + map { $_ => $code } + @$methods + } + ] + ); + } + return $self; +} + +sub generate_text_elements { + shift->generate_ops( + { methods => 'text value values' }, + @_ + ); +} + + + +1; +__END__ + +sub parse_expr { + shift->debug("parse_expr"); +} + +1; + +__END__ +This module should define a metaclass module for constructing elements. + +Current element construction looks something like this: + + use Template::TT3::Class + version => 3.00, + base => 'Template::TT3::Element', + constants => ':elements', + as => 'filename', + view => 'literal', + constant => { + SEXPR_FORMAT => '', + }, + alias => { + parse_word => 'advance', + name => \&text, + value => \&text, + values => \&text, + source => \&text, + }; + +We should be able to reduce it to something like this: + + use Template::TT3::Element::Class + version => 3.00, + type => 'literal', + parse_expr => 'filename', + alias_to => { + text => 'name value values source' + }; + +Implicit actions: + + * add element base class + * export :elements constants (rename this while we're at it) + * type implies view + +Another current example: + + use Template::TT3::Class + version => 3.00, + debug => 0, + base => 'Template::TT3::Element::Operator::InfixRight + Template::TT3::Element::Operator::Assignment + Template::TT3::Element', + import => 'class', + as => 'pair', + constants => ':elements', + constant => { + SEXPR_FORMAT => '<%s>>', + }, + alias => { + as_pair => 'self', # I can do pairs, me + number => \&value, # FIXME + values => \&value, # TODO: parallel assignment + }; + +Should be: + + use Template::TT3::Class + version => 3.00, + debug => 0, + parse => { + infix => 'right', + assign => 1, # er.... not sure + } + as => 'pair', # better name or is this OK? + alias_to => { + value => 'number values' + self => 'as_pair', # should be implicit + }; diff --git a/lib/Template/TT3/Factory/Class.pm b/lib/Template/TT3/Class/Factory.pm similarity index 91% rename from lib/Template/TT3/Factory/Class.pm rename to lib/Template/TT3/Class/Factory.pm index f795a17..4c09faf 100644 --- a/lib/Template/TT3/Factory/Class.pm +++ b/lib/Template/TT3/Class/Factory.pm @@ -1,7 +1,7 @@ -package Template::TT3::Factory::Class; +package Template::TT3::Class::Factory; use Template::TT3::Class - version => 0.01, + version => 2.69, debug => 0, uber => 'Badger::Factory::Class', constant => { @@ -14,7 +14,7 @@ __END__ =head1 NAME -Template::TT3::Factory::Class - class constructor for factory modules +Template::TT3::Class::Factory - class constructor for factory modules =head1 SYNOPSIS @@ -23,7 +23,7 @@ Template::TT3::Factory::Class - class constructor for factory modules package Template::TT3::Engines; - use Template::TT3::Factory::Class + use Template::TT3::Class::Factory version => 3.00, debug => 0, item => 'engine', diff --git a/lib/Template/TT3/Constants.pm b/lib/Template/TT3/Constants.pm index 6c193b1..52ff538 100644 --- a/lib/Template/TT3/Constants.pm +++ b/lib/Template/TT3/Constants.pm @@ -80,8 +80,6 @@ use Badger::Class # some dupliction here - needs sorting out SELF => '=0', # zeroth argument is always $self }, - - elements => { # element slots - the first 4 are common to all elements META => '=0', diff --git a/lib/Template/TT3/Dialects.pm b/lib/Template/TT3/Dialects.pm index 504bc56..c795e73 100644 --- a/lib/Template/TT3/Dialects.pm +++ b/lib/Template/TT3/Dialects.pm @@ -1,6 +1,6 @@ package Template::TT3::Dialects; -use Template::TT3::Factory::Class +use Template::TT3::Class::Factory version => 2.71, debug => 0, constants => 'HASH', @@ -179,6 +179,9 @@ modify it under the same terms as Perl itself. This module inherits methods from the L, L, L, and L base classes. +It is constructed using the L class +metaprogramming module. + It loads modules and instantiates object that are subclasses of L. See L for an example. diff --git a/lib/Template/TT3/Element/Command/Fill.pm b/lib/Template/TT3/Element/Command/Fill.pm index 1946957..3a9f68a 100644 --- a/lib/Template/TT3/Element/Command/Fill.pm +++ b/lib/Template/TT3/Element/Command/Fill.pm @@ -1,18 +1,19 @@ package Template::TT3::Element::Command::Fill; -use Template::TT3::Class +use Template::TT3::Class::Element version => 3.00, debug => 0, base => 'Template::TT3::Element::Keyword', - as => 'name_expr', + parse_expr => 'name', + type => 'fill', view => 'fill', - constants => ':elements ARRAY', + constants => 'ARRAY', constant => { SEXPR_FORMAT => "", }, alias => { - values => \&text, value => \&text, + values => \&text, }; sub sexpr { diff --git a/lib/Template/TT3/Element/Comment.pm b/lib/Template/TT3/Element/Comment.pm new file mode 100644 index 0000000..6e8b7b3 --- /dev/null +++ b/lib/Template/TT3/Element/Comment.pm @@ -0,0 +1,11 @@ +package Template::TT3::Element::Comment; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Whitespace', + view => 'comment'; + + +1; + diff --git a/lib/Template/TT3/Element/Delimiter.pm b/lib/Template/TT3/Element/Delimiter.pm new file mode 100644 index 0000000..b3070df --- /dev/null +++ b/lib/Template/TT3/Element/Delimiter.pm @@ -0,0 +1,77 @@ +package Template::TT3::Element::Delimiter; + +use Template::TT3::Class + version => 3.00, + debug => 0, + base => 'Template::TT3::Element', + constants => ':elements BLANK', + constant => { + FINISH => 'end', + }, + alias => { + parse_expr => 'null', + }; + + + +sub skip_delimiter { + $_[SELF]->next_skip_ws( $_[CONTEXT] ) + ->skip_delimiter( $_[CONTEXT] ); +} + + +sub parse_body { + my ($self, $token, $scope, $parent, $follow) = @_; + my (@exprs, $expr, $begfrag, $endfrag, $fragtok); + + $parent ||= $self; + + $self->debug("parse_body(), parent is $parent->[TOKEN]") if DEBUG; + + # skip past token any whitespace, then parse expressions + my $block = $$token->next_skip_ws($token)->parse_block($token, $scope) + || return $parent->fail_missing($self->ARG_BLOCK, $token); + + # if the parent defines any follow-on blocks (e.g. elsif/else for if) + # then we look to see if the next token is one of them and activate it + + # TODO: check that it's safe to assume $follow is always $parent->FOLLOW + # my $follow = $parent->FOLLOW; + + if ($follow && $$token->skip_ws($token)->in($follow)) { + $self->debug("Found follow-on token: ", $$token->token) if DEBUG; + return $$token->parse_follow($block, $token, $scope, $parent); + } + + # otherwise the next token must be our FINISH token (e.g. end) + my $finish = $self->FINISH; + my $endtok = $$token->skip_ws($token); + + return $parent->fail_missing($finish, $token) + unless $$token->is($finish, $token); + + # if a fragment follows then it must match the parent command token + # (e.g. for ... end#for) or the explicit fragment added to the token + # passed to us as $fragment (e.g. for#outer ... end#outer) + if ($fragtok = $$token->parse_fragment($token, $scope)) { + $begfrag = $parent->[FRAGMENT] ? $parent->[FRAGMENT]->[TOKEN] : BLANK; + $endfrag = $fragtok->[TOKEN]; + + $self->debug( + "comparing end fragment: [$endfrag] to parent [$parent->[TOKEN]] [$begfrag]" + ) if DEBUG; + + return ($fragtok || $endtok)->fail_syntax( + bad_fragment => $parent->[TOKEN] . ($begfrag ? '#' . $begfrag : ''), + $endtok->[TOKEN] . '#' . $endfrag + ) + unless $parent->[TOKEN] eq $endfrag + or $begfrag && $begfrag eq $endfrag; + } + + # return the $block we built, not $self which is the delimiter + return $block; +} + + +1; diff --git a/lib/Template/TT3/Element/Dquote.pm b/lib/Template/TT3/Element/Dquote.pm new file mode 100644 index 0000000..88e34f7 --- /dev/null +++ b/lib/Template/TT3/Element/Dquote.pm @@ -0,0 +1,50 @@ +package Template::TT3::Element::Dquote; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::String', + view => 'dquote', + alias => { + value => \&text, + values => \&text, + parse_filename => \&parse_expr, + }; + + +sub parse_expr { + my ($self, $token, $scope) = @_; + my $branch = $self->[BRANCH]; + + $self->advance($token); + + if ($branch) { + $self->[BLOCK] = $branch->parse_block(\$branch, $scope) + || $self->fail_missing( branch => $branch ); + + my $junk = $branch->remaining_text; + return $self->error("Trailing text in double quoted string branch: $junk") + if defined $junk && length $junk; + + $self->debug( + "compiled double quoted string branch: ", + $self->[BLOCK]->source, + ) if DEBUG; + } + + return $$token->skip_ws($token) + ->parse_infix($self, $token, $scope, $self->[META]->[LPREC]); +} + + + +sub text { + # If we have a BLOCK then this is a dynamic string, e.g. "foo $bar" + # otherwise it's a static string in EXPR + $_[SELF]->[BLOCK] + ? $_[SELF]->[BLOCK]->text($_[CONTEXT]) + : $_[SELF]->[EXPR] +} + + +1; diff --git a/lib/Template/TT3/Element/End.pm b/lib/Template/TT3/Element/End.pm new file mode 100644 index 0000000..09af90b --- /dev/null +++ b/lib/Template/TT3/Element/End.pm @@ -0,0 +1,10 @@ +package Template::TT3::Element::End; + +use Template::TT3::Class::Element + version => 2.69, + base => 'Template::TT3::Element::Terminator', + view => 'keyword'; + + +1; + diff --git a/lib/Template/TT3/Element/Eof.pm b/lib/Template/TT3/Element/Eof.pm new file mode 100644 index 0000000..634ef1d --- /dev/null +++ b/lib/Template/TT3/Element/Eof.pm @@ -0,0 +1,16 @@ +package Template::TT3::Element::Eof; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element', + view => 'eof', + constant => { + eof => 1, + }, + alias => { + parse_expr => 'null', + }; + + +1; diff --git a/lib/Template/TT3/Element/Fragment.pm b/lib/Template/TT3/Element/Fragment.pm new file mode 100644 index 0000000..72f051b --- /dev/null +++ b/lib/Template/TT3/Element/Fragment.pm @@ -0,0 +1,16 @@ +package Template::TT3::Element::Fragment; + +use Template::TT3::Class::Element + version => 2.69, + base => 'Template::TT3::Element::Terminator', + view => 'fragment'; + + +sub parse_fragment { + my ($self, $token, $scope) = @_; + $$token = $self->[NEXT]; + return $$token->parse_word($token, $scope) + || $self->fail_missing( word => $token ); +} + +1; diff --git a/lib/Template/TT3/Element/Literal.pm b/lib/Template/TT3/Element/Literal.pm index 44f901c..0fac267 100644 --- a/lib/Template/TT3/Element/Literal.pm +++ b/lib/Template/TT3/Element/Literal.pm @@ -1,55 +1,57 @@ package Template::TT3::Element::Literal; -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element', - constants => ':elements', - as => 'filename', - view => 'literal', - constant => { - SEXPR_FORMAT => '', +use Template::TT3::Class::Element + version => 2.69, + roles => 'filename', + base => 'Template::TT3::Element', + constant => { + SOURCE_FORMAT => '%s', }, - alias => { + alias => { + parse_expr => 'advance', parse_word => 'advance', - name => \&text, - value => \&text, - values => \&text, - source => \&text, + text => \&token, + value => \&token, + values => \&token, }; -sub text { - $_[0]->[TOKEN]; +sub token { + $_[SELF]->[TOKEN]; } -sub sexpr { - sprintf( - $_[0]->SEXPR_FORMAT, - $_[0]->[TOKEN] +sub variable { + # literal values can be converted to a text variable in order to perform + # dotops or other stringy operations on it + $_[CONTEXT]->use_var( + $_[SELF], $_[SELF]->text( $_[CONTEXT] ) ); } -sub old_generate { - $_[1]->generate_literal( +sub source { + sprintf( + $_[0]->SOURCE_FORMAT, $_[0]->[TOKEN] ); } + 1; __END__ =head1 NAME -Template:TT3::Element::Construct - base class element for literal elements +Template:TT3::Element::Literal - base class element for literal elements =head1 DESCRIPTION This module implements a subclass of L. It acts as a -common base class for the L, L -and L modules. +common base class for the L, +L and L +modules. =head1 METHODS @@ -59,19 +61,29 @@ base classes. =head2 parse_word() +This method is an alias to the L +method inherited from the L base class. + =head2 text() +This method is an alias to the L +method inherited from the L base class. It simply +returns the literal token text. + =head2 value() -An alias to L. +An alias as per L. =head2 values() -An alias to L. +An alias as per L. =head2 name() -An alias to L. +An alias as per L. + +TODO: I've taken this out... I don't think we're using name() any more... +let's see what breaks. =head1 AUTHOR diff --git a/lib/Template/TT3/Element/Number.pm b/lib/Template/TT3/Element/Number.pm new file mode 100644 index 0000000..6d07a57 --- /dev/null +++ b/lib/Template/TT3/Element/Number.pm @@ -0,0 +1,28 @@ +package Template::TT3::Element::Number; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Literal', + view => 'number', + alias => { +# parse_number => 'self', # this is already a number op +# parse_dotop => 'advance', + number => 'value', # our token contains the number + }; + +sub parse_expr { + my ($self, $token, $scope, $prec) = @_; + + # advance token + $$token = $self->[NEXT]; + + # numbers can be followed by infix operators, e.g. 400 + 20 + return $$token->skip_ws->parse_infix($self, $token, $scope, $prec); +} + +sub sexpr { + return '[TOKEN] . '>'; +} + +1; diff --git a/lib/Template/TT3/Elements/Boolean.pm b/lib/Template/TT3/Element/Operator/Boolean.pm similarity index 66% rename from lib/Template/TT3/Elements/Boolean.pm rename to lib/Template/TT3/Element/Operator/Boolean.pm index 5e36445..469a7d1 100644 --- a/lib/Template/TT3/Elements/Boolean.pm +++ b/lib/Template/TT3/Element/Operator/Boolean.pm @@ -1,36 +1,39 @@ -package Template::TT3::Element::Boolean; +package Template::TT3::Element::Operator::Boolean; use Template::TT3::Elements::Operator; -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element', - import => 'class', - constants => ':elements'; +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + import => 'class'; #----------------------------------------------------------------------- -# Call on generate_bool_ops() (in Template::TT3::Class) to create a -# bunch of subclasses of Template::TT3::Element::Boolean. See the comment -# for generate_number_ops() in Template::TT3::Elements::Number for +# Call on generate_elements() (in Template::TT3::Class::Element) to +# create a bunch of subclasses of Template::TT3::Element::Operator::Text. +# See the comments in Template::TT3::Elements::Operator::Number for # further discussion. For boolean ops we alias the subroutine specified -# as value() and values(). +# as value() and values(). We inherit the default text() method from +# the element base class. #----------------------------------------------------------------------- -class->generate_boolean_ops( +class->generate_elements( + { + methods => 'value values' + }, 'not' => prefix => sub { # ! a return - ! $_[0]->[RHS]->maybe($_[1]) + ! $_[0]->[RHS]->value($_[1]) }, 'and' => infix_left => sub { # a && b return $_[0]->[LHS]->value($_[1]) && $_[0]->[RHS]->value($_[1]) }, 'or' => infix_left => sub { # a || b - return $_[0]->[LHS]->maybe($_[1]) + return $_[0]->[LHS]->value($_[1]) || $_[0]->[RHS]->value($_[1]) }, 'nor' => infix_left => sub { # a !! b - my $value = $_[0]->[LHS]->maybe($_[1]); + my $value = $_[0]->[LHS]->value($_[1]); return defined $value ? $value : $_[0]->[RHS]->value($_[1]) @@ -45,12 +48,12 @@ class->generate_boolean_ops( or_set => infix_right => assignment => sub { # a ||= b return $_[0]->[LHS]->assign( $_[1], - $_[0]->[LHS]->maybe($_[1]) + $_[0]->[LHS]->value($_[1]) || $_[0]->[RHS]->value($_[1]) )->value; }, nor_set => infix_right => assignment => sub { # a !!= b - my $value = $_[0]->[LHS]->maybe($_[1]); + my $value = $_[0]->[LHS]->value($_[1]); return defined $value ? $value : $_[0]->[LHS]->assign( diff --git a/lib/Template/TT3/Elements/Number.pm b/lib/Template/TT3/Element/Operator/Number.pm similarity index 53% rename from lib/Template/TT3/Elements/Number.pm rename to lib/Template/TT3/Element/Operator/Number.pm index 28cf323..4d50d02 100644 --- a/lib/Template/TT3/Elements/Number.pm +++ b/lib/Template/TT3/Element/Operator/Number.pm @@ -1,33 +1,24 @@ -package Template::TT3::Element::Number; +package Template::TT3::Element::Operator::Number; -use Template::TT3::Elements::Operator; -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Literal', +use Template::TT3::Elements::Operator; # FIXME +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element', # TODO: +Operator import => 'class', - view => 'number', - constants => ':elements', constant => { - SEXPR_FORMAT => '', - }, - alias => { - parse_number => 'self', # this is already a number op -# parse_dotop => 'advance', - number => 'value', # our token contains the number + SEXPR_FORMAT => '', # TODO: remove me }; -sub parse_expr { - my ($self, $token, $scope, $prec) = @_; - - # advance token - $$token = $self->[NEXT]; - - # variables can be followed by postops (postfix and infix operators) - return $$token->skip_ws->parse_infix($self, $token, $scope, $prec); +sub variable { + # variable operations can be converted to a variable in order to + # perform dotops on it + $_[CONTEXT]->use_var( + $_[SELF], $_[SELF]->value( $_[CONTEXT] ) + ); } - sub sexpr { sprintf( $_[SELF]->SEXPR_FORMAT, @@ -36,32 +27,22 @@ sub sexpr { } -sub source { - $_[SELF]->[TOKEN]; -} - - -sub generate { - $_[CONTEXT]->generate_number( - $_[SELF]->[TOKEN] - ); -} - - -## NOTE: I don't think we should make all the number ops a subclass of -## T::Element::Number / Literal. - #----------------------------------------------------------------------- -# Call on generate_number_ops() (in Template::TT3::Class) to create a -# bunch of subclasses of Template::TT3::Element::Number. The first -# argument is a class name (e.g. pre_inc) which gets CamelCased and -# added to the base (e.g. Template::TT3::Element::Number::PreInc). Any -# subsequent names are operator base classes. These are also CamelCased -# and added to the operator base class name (e.g. 'infix_right' becomes -# Template::TT3::Element::Operator::InfixRight). Then we have a code -# reference which implements the value() method for the operator. For -# number operators this is also aliased as values(), number() and text(). +# Use generate_elements() (in Template::TT3::Class::Element) to create +# a bunch of subclasses of Template::TT3::Element::Operator::Number. +# +# The first argument is a class name (e.g. pre_inc) which gets CamelCased +# and added to the base class package (e.g. 'pre_inc' is mapped to +# Template::TT3::Element::Operator::Number::PreInc). Any subsequent names +# are operator base classes. These are also CamelCased and added to the +# operator base class name (e.g. 'infix_right' becomes +# Template::TT3::Element::Operator::InfixRight). +# +# Then we have a code reference which implements the value() method for +# the operator. For numerical operators this is also aliased as values(), +# number() and text(). +# # All operands to numerical operators (e.g. EXPR, LHS and RHS) must # yield numerical values, so we call number() rather than value(). If # they are already numerical expressions then we'll get the shortcut to @@ -71,8 +52,11 @@ sub generate { # directly (e.g. $_[SELF]) wherever possible. #----------------------------------------------------------------------- -BEGIN { -class->generate_number_ops( +class->generate_elements( + { + methods => 'text number value values' + }, + positive => prefix => sub { # +a return + $_[SELF]->[RHS]->number($_[CONTEXT]); @@ -104,14 +88,12 @@ class->generate_number_ops( % $_[SELF]->[RHS]->number($_[CONTEXT]) }, add => infix_left => sub { # a + b - return - $_[SELF]->[LHS]->number($_[CONTEXT]) - + $_[SELF]->[RHS]->number($_[CONTEXT]); + return $_[SELF]->[LHS]->number($_[CONTEXT]) + + $_[SELF]->[RHS]->number($_[CONTEXT]); }, subtract => infix_left => sub { # a - b - return - $_[SELF]->[LHS]->number($_[CONTEXT]) - - $_[SELF]->[RHS]->number($_[CONTEXT]); + return $_[SELF]->[LHS]->number($_[CONTEXT]) + - $_[SELF]->[RHS]->number($_[CONTEXT]); }, equal => infix => sub { # a == b return $_[SELF]->[LHS]->number($_[CONTEXT]) @@ -141,23 +123,36 @@ class->generate_number_ops( return $_[SELF]->[LHS]->number($_[CONTEXT]) <=> $_[SELF]->[RHS]->number($_[CONTEXT]); }, - - # TODO: these should be $lhs->variable->set(...) + ); + #----------------------------------------------------------------------- -# A call to generate_number_assign_ops() which performs much the same -# task as generate_number_ops() but inherits the text() method from the -# T::TT3::Element::Operator::Assignment base class instead of aliasing -# it to the value() method. +# Same again, but without aliasing the function to the text() method. +# Instead we inherit the text() method from the T~Operator::Assignment +# base class which performs the assignment (by calling $self->value()) +# but returns an empty list. This is how we silence assignment operators +# from generating any output in "text context", e.g. [% a = 10 %] #----------------------------------------------------------------------- -class->generate_number_assign_ops( +class->generate_elements( + { + methods => 'number value values' + }, + pre_inc => prefix => assignment => sub { # ++a - return $_[SELF]->[RHS]->assign( - $_[CONTEXT], - $_[SELF]->[RHS]->number($_[CONTEXT]) + 1 - )->value; + return + $_[SELF]->[RHS]->assign( + $_[CONTEXT], + $_[SELF]->[RHS]->number($_[CONTEXT]) + 1 + )->value; + }, + pre_dec => prefix => assignment => sub { # --a + return + $_[SELF]->[RHS]->assign( + $_[CONTEXT], + $_[SELF]->[RHS]->number($_[CONTEXT]) - 1 + )->value; }, post_inc => postfix => assignment => sub { # a++ my $n = $_[SELF]->[LHS]->number($_[CONTEXT]); @@ -167,12 +162,6 @@ class->generate_number_assign_ops( ); return $n; }, - pre_dec => prefix => assignment => sub { # --a - return $_[SELF]->[RHS]->assign( - $_[CONTEXT], - $_[SELF]->[RHS]->number($_[CONTEXT]) - 1 - )->value; - }, post_dec => postfix => assignment => sub { # a-- my $n = $_[SELF]->[LHS]->number($_[CONTEXT]); $_[SELF]->[LHS]->assign( @@ -182,39 +171,42 @@ class->generate_number_assign_ops( return $n; }, add_set => infix_right => assignment => sub { # a += b - return $_[SELF]->[LHS]->assign( - $_[CONTEXT], - $_[SELF]->[LHS]->number($_[CONTEXT]) - + $_[SELF]->[RHS]->number($_[CONTEXT]) - )->value; + return + $_[SELF]->[LHS]->assign( + $_[CONTEXT], + $_[SELF]->[LHS]->number($_[CONTEXT]) + + $_[SELF]->[RHS]->number($_[CONTEXT]) + )->value; }, sub_set => infix_right => assignment => sub { # a -= b - return $_[SELF]->[LHS]->assign( - $_[CONTEXT], - $_[SELF]->[LHS]->number($_[CONTEXT]) - - $_[SELF]->[RHS]->number($_[CONTEXT]) - )->value; + return + $_[SELF]->[LHS]->assign( + $_[CONTEXT], + $_[SELF]->[LHS]->number($_[CONTEXT]) + - $_[SELF]->[RHS]->number($_[CONTEXT]) + )->value; }, mul_set => infix_right => assignment => sub { # a *= b - return $_[SELF]->[LHS]->assign( - $_[CONTEXT], - $_[SELF]->[LHS]->number($_[CONTEXT]) - * $_[SELF]->[RHS]->number($_[CONTEXT]) - )->value; + return + $_[SELF]->[LHS]->assign( + $_[CONTEXT], + $_[SELF]->[LHS]->number($_[CONTEXT]) + * $_[SELF]->[RHS]->number($_[CONTEXT]) + )->value; }, div_set => infix_right => assignment => sub { # a /= b - return $_[SELF]->[LHS]->assign( - $_[CONTEXT], - $_[SELF]->[LHS]->number($_[CONTEXT]) - / $_[SELF]->[RHS]->number($_[CONTEXT]) - )->value; + return + $_[SELF]->[LHS]->assign( + $_[CONTEXT], + $_[SELF]->[LHS]->number($_[CONTEXT]) + / $_[SELF]->[RHS]->number($_[CONTEXT]) + )->value; }, ); -} #----------------------------------------------------------------------- -# Another call to generate_pre_post_ops() which defines operator classes +# A call to generate_pre_post_ops() which defines operator classes # that can be either prefix operators or postfix operators. e.g. '-' # and '+' can be prefix or postfix (infix). # @@ -232,72 +224,15 @@ class->generate_pre_post_ops( dec => ['num_pre_dec', 'num_post_dec'], plus => ['num_positive', 'num_add'], minus => ['num_negative', 'num_subtract'], - -# NOTE: We no longer have '%' masquerading as two different operators based -# on being a prefix operator (hash expansion: %foo) or an infix operator -# (modulus: a % 10). Now we have two separate operators, '%' for prefix -# hash expansion and ' % ' for infix modulus. -# percent => ['sig_hash', 'num_modulus'], ); - -#----------------------------------------------------------------------- -# range -#----------------------------------------------------------------------- - -package Template::TT3::Element::Number::Range; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Operator::Infix - Template::TT3::Element', - constants => ':elements BLANK'; - -sub text { - join(BLANK, @{ $_[SELF]->value($_[CONTEXT]) }); -} - -sub values { - @{ $_[SELF]->value($_[CONTEXT]) }; -} - -sub value { - [ - $_[SELF]->[LHS]->number($_[CONTEXT]) - .. $_[SELF]->[RHS]->number($_[CONTEXT]) - ] -} - - - #----------------------------------------------------------------------- -# '/' can be used as a filename separator: foo/bar.tt3 +# Tweak: '/' can be used as a filename separator: foo/bar.tt3 #----------------------------------------------------------------------- -package Template::TT3::Element::Number::Divide; - -use Template::TT3::Class - as => 'filename'; - - -#----------------------------------------------------------------------- -# special cases for *, / and % which can be used in places other than as -# binary operators. -#----------------------------------------------------------------------- - -package Template::TT3::Element::Number::Multiply; - - -package Template::TT3::Element::Number::Percent; - -use Template::TT3::Class - base => 'Template::TT3::Element::Number::Modulus'; - -package Template::TT3::Element::Number::To; - -use Template::TT3::Class - base => 'Template::TT3::Element::Number::Range'; +class->subclass('divide') + ->roles('filename'); 1; diff --git a/lib/Template/TT3/Element/Operator/Range.pm b/lib/Template/TT3/Element/Operator/Range.pm new file mode 100644 index 0000000..e47ca1a --- /dev/null +++ b/lib/Template/TT3/Element/Operator/Range.pm @@ -0,0 +1,29 @@ +package Template::TT3::Element::Operator::Range; + +use Template::TT3::Elements::Operator; +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Operator::Infix', + constants => ':elements BLANK'; + + +sub text { + join( + BLANK, + @{ $_[SELF]->value($_[CONTEXT]) } + ); +} + +sub values { + @{ $_[SELF]->value($_[CONTEXT]) }; +} + +sub value { + [ + $_[SELF]->[LHS]->number($_[CONTEXT]) + .. $_[SELF]->[RHS]->number($_[CONTEXT]) + ] +} + +1; diff --git a/lib/Template/TT3/Element/Operator/Text.pm b/lib/Template/TT3/Element/Operator/Text.pm new file mode 100644 index 0000000..c5dfe66 --- /dev/null +++ b/lib/Template/TT3/Element/Operator/Text.pm @@ -0,0 +1,103 @@ +package Template::TT3::Element::Operator::Text; + +use Template::TT3::Elements::Operator; # FIXME +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element', # TODO: +Operator + import => 'class'; + + +sub variable { + # text operation can be converted to a text variable in order to + # perform dotops on it + $_[CONTEXT]->use_var( + $_[SELF], $_[SELF]->text( $_[CONTEXT] ) + ); +} + + +#----------------------------------------------------------------------- +# Call on generate_elements() (in Template::TT3::Class::Element) to +# create a bunch of subclasses of Template::TT3::Element::Operator::Text. +# See the comments in Template::TT3::Elements::Operator::Number for +# further discussion. For text ops we alias the subroutine specified +# as value(), values() and text(). +#----------------------------------------------------------------------- + +class->generate_elements( + { + methods => 'text value values' + }, + + convert => prefix => sub { # ~ b + return $_[0]->[EXPR]->text($_[1]) + }, + combine => infix_left => sub { # a ~ b + return $_[0]->[LHS]->text($_[1]) + . $_[0]->[RHS]->text($_[1]) + }, + equal => infix => sub { + return $_[0]->[LHS]->text($_[1]) # a eq b + eq $_[0]->[RHS]->text($_[1]) + }, + not_equal => infix => sub { # a ne b + return $_[0]->[LHS]->text($_[1]) + ne $_[0]->[RHS]->text($_[1]) + }, + less_than => infix => sub { # a lt b + return $_[0]->[LHS]->text($_[1]) + lt $_[0]->[RHS]->text($_[1]) + }, + more_than => infix => sub { # a gt b + return $_[0]->[LHS]->text($_[1]) + gt $_[0]->[RHS]->text($_[1]) + }, + less_equal => infix => sub { # a lt b + return $_[0]->[LHS]->text($_[1]) + le $_[0]->[RHS]->text($_[1]) + }, + more_equal => infix => sub { # a ge b + return $_[0]->[LHS]->text($_[1]) + ge $_[0]->[RHS]->text($_[1]) + }, + compare => infix => sub { # a cmp b + return $_[0]->[LHS]->text($_[1]) + cmp $_[0]->[RHS]->text($_[1]) + }, +); + + +#----------------------------------------------------------------------- +# Same again, but without aliasing the function to the text() method. +# Instead we inherit the text() method from the T~Operator::Assignment +# base class which performs the assignment (by calling $self->value()) +# but returns an empty list. This is how we silence assignment operators +# from generating any output in "text context", e.g. [% a = 10 %] +#----------------------------------------------------------------------- + +class->generate_elements( + { + methods => 'value values' + }, + + combine_set => infix_right => assignment => sub { # a ~= b + return $_[0]->[LHS]->assign( + $_[1], + $_[0]->[LHS]->text($_[1]) + . $_[0]->[RHS]->text($_[1]) + ); + }, +); + + +#----------------------------------------------------------------------- +# A call to generate_pre_post_ops() which defines operator classes +# that can be either prefix operators or postfix operators. e.g. '~' +#----------------------------------------------------------------------- + +class->generate_pre_post_ops( + squiggle => ['txt_convert', 'txt_combine'], +); + +1; diff --git a/lib/Template/TT3/Element/Padding.pm b/lib/Template/TT3/Element/Padding.pm new file mode 100644 index 0000000..324892a --- /dev/null +++ b/lib/Template/TT3/Element/Padding.pm @@ -0,0 +1,24 @@ +package Template::TT3::Element::Padding; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Text', + view => 'padding'; + + +1 +__END__ + +#----------------------------------------------------------------------- +# Template::TT3::Element::Padding - a thin subclass of the text element +# used to represent sythesised text tokens added as part of the scanning +# process. For example, the '=' pre and post chomp flags collapse any +# preceding/following text to a single space. We save the original +# whitespace (of which there may be none) as a whitespace token and add +# new padding token of a single space. When we want to re-generate the +# original template source we print out the whitespace but ignore padding. +# OTOH, when we're parsing we ignore whitespace but include padding as +# kind of text expression. +#----------------------------------------------------------------------- + diff --git a/lib/Template/TT3/Element/Role/Expr/Name.pm b/lib/Template/TT3/Element/Role/Expr/Name.pm new file mode 100644 index 0000000..a59f3a5 --- /dev/null +++ b/lib/Template/TT3/Element/Role/Expr/Name.pm @@ -0,0 +1,33 @@ +package Template::TT3::Element::Role::Expr::Name; + +use Template::TT3::Class + version => 2.718, + mixins => 'parse_expr', + constants => ':elements'; + + +sub parse_expr { + my ($self, $token, $scope, $prec, $force) = @_; + + # Operator precedence. + return undef + if $prec && ! $force && $self->[META]->[LPREC] <= $prec; + + # Advance past the keyword and whitespace, then parse a filename + $self->[EXPR] = $$token + ->next_skip_ws($token) + ->parse_filename($token, $scope, $self->[META]->[LPREC]) + || return $self->fail_missing( $self->ARG_NAME => $token ); + + # Save the scope so we can lookup lexically scoped blocks later + # NOTE: there is no guarantee this this method won't get mixed into + # a module which does something else with the ARGS slot. + $self->[ARGS] = $scope; + + # Parse any infix operators following this expression + return $$token->skip_ws->parse_infix($self, $token, $scope, $prec); +} + + +1; + diff --git a/lib/Template/TT3/Element/Role/Filename.pm b/lib/Template/TT3/Element/Role/Filename.pm index c1fc3c8..1e9ffb6 100644 --- a/lib/Template/TT3/Element/Role/Filename.pm +++ b/lib/Template/TT3/Element/Role/Filename.pm @@ -1,7 +1,7 @@ package Template::TT3::Element::Role::Filename; use Template::TT3::Class - version => 2.718, + version => 2.69, constants => ':elements', mixins => 'parse_filename filename'; @@ -10,24 +10,23 @@ sub parse_filename { my ($self, $token) = @_; my $next; - # advance token + # Our token forms the start of the filename + $self->[EXPR] = $self->[TOKEN]; + + # Advance token pointer to the next token $$token = $self->[NEXT]; - # our token forms the start of the filename - $self->[EXPR] = $self->[TOKEN]; - - # add any subsequent filename tokens onto the filename + # Append any subsequent filename elements $self->[EXPR] .= $next->[EXPR] if $next = $$token->parse_filename($token); -# $self->debug("filename expr: $self->[EXPR]"); - - # rebless self into a filename token + # Rebless self into a filename element $self->become('filename'); - + return $self; } + sub filename { $_[SELF]->[EXPR]; } diff --git a/lib/Template/TT3/Element/Role/NameExpr.pm b/lib/Template/TT3/Element/Role/NameExpr.pm index 58ce4a1..5698eb5 100644 --- a/lib/Template/TT3/Element/Role/NameExpr.pm +++ b/lib/Template/TT3/Element/Role/NameExpr.pm @@ -1,5 +1,8 @@ package Template::TT3::Element::Role::NameExpr; +use Carp; +croak __PACKAGE__, ' is deprecated - use Template::TT3::Element::Role::Expr::Name instead'; + use Template::TT3::Class version => 2.718, mixins => 'parse_expr', diff --git a/lib/Template/TT3/Element/Separator.pm b/lib/Template/TT3/Element/Separator.pm new file mode 100644 index 0000000..a1bbddf --- /dev/null +++ b/lib/Template/TT3/Element/Separator.pm @@ -0,0 +1,28 @@ +package Template::TT3::Element::Separator; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element'; + + +sub parse_expr { + my ($self, $token, @args) = @_; + $$token = $self->[NEXT] if $token; + $self->[NEXT]->parse_expr($token, @args); +} + + +sub skip_separator { + $_[SELF]->next_skip_ws( $_[CONTEXT] ) + ->skip_separator( $_[CONTEXT] ); +} + + +sub skip_delimiter { + $_[SELF]->next_skip_ws( $_[CONTEXT] ) + ->skip_delimiter( $_[CONTEXT] ); +} + + +1; diff --git a/lib/Template/TT3/Element/Squote.pm b/lib/Template/TT3/Element/Squote.pm new file mode 100644 index 0000000..585aaef --- /dev/null +++ b/lib/Template/TT3/Element/Squote.pm @@ -0,0 +1,10 @@ +package Template::TT3::Element::Squote; + +use Template::TT3::Class::Element + version => 2.69, + base => 'Template::TT3::Element::String', + view => 'squote'; + + +1; + diff --git a/lib/Template/TT3/Element/String.pm b/lib/Template/TT3/Element/String.pm new file mode 100644 index 0000000..82f97d1 --- /dev/null +++ b/lib/Template/TT3/Element/String.pm @@ -0,0 +1,57 @@ +package Template::TT3::Element::String; + +use Template::TT3::Class::Element + version => 2.69, + base => 'Template::TT3::Element::Text', + view => 'string', + constant => { + SOURCE_FORMAT => '%s', + }, + alias => { + value => \&text, + values => \&text, + parse_filename => \&parse_expr, + }; + + +sub parse_expr { + my ($self, $token, $scope, $prec) = @_; + + # copy original TEXT into EXPR in case we don't already have a + # reduced form (i.e. without quotes) + $self->[EXPR] = $self->[TOKEN] + unless defined $self->[EXPR]; + + # strings can be followed by postops (postfix and infix operators) + return $$token->next_skip_ws($token) + ->parse_infix($self, $token, $scope, $prec); +} + + +sub text { + $_[SELF]->[EXPR]; +} + + +sub filename { + $_[SELF]->text( $_[CONTEXT] ); +} + + +sub template { + my $self = shift; + return $self->fetch_template( + $self->text(@_), @_ + ); +} + + +sub variable { + $_[CONTEXT]->use_var( + $_[SELF], + $_[SELF]->text( $_[CONTEXT] ) + ); +} + + +1; diff --git a/lib/Template/TT3/Element/TagEnd.pm b/lib/Template/TT3/Element/TagEnd.pm new file mode 100644 index 0000000..bafd76a --- /dev/null +++ b/lib/Template/TT3/Element/TagEnd.pm @@ -0,0 +1,9 @@ +package Template::TT3::Element::TagEnd; + +use Template::TT3::Class::Element + version => 2.69, + base => 'Template::TT3::Element::Delimiter', + view => 'tag_end'; + +1; + diff --git a/lib/Template/TT3/Element/TagStart.pm b/lib/Template/TT3/Element/TagStart.pm new file mode 100644 index 0000000..05ae19c --- /dev/null +++ b/lib/Template/TT3/Element/TagStart.pm @@ -0,0 +1,30 @@ +package Template::TT3::Element::TagStart; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Whitespace', + view => 'tag_start', + alias => { + skip_ws => \&next_skip_ws, + }; + + +sub next_skip_ws { + # In the case of scan-time control directives (e.g. [? TAGS '<* *>' ?] ) + # we want to hide the tokens inside the directive from the expression + # parser because they have already been interpreted at tokenising time + # and don't equate to runtime expressions that the parser understands. + # So the tokeniser for these tags adds a BRANCH entry in the start token + # for the directive that points to the end token. Whenever we skip_ws + # or next_skip_ws on one of these start tokens (as we always do when + # a whitespace token parse_expr() method is called) then we jump straight + # down to the end token and continue from there. For regular tags, we + # just advance to the next token as usual. + return ($_[0]->[BRANCH] && $_[0]->[BRANCH]->skip_ws($_[1])) + || ($_[0]->[NEXT] && $_[0]->[NEXT]->skip_ws($_[1])) +} + +1; + + diff --git a/lib/Template/TT3/Element/Terminator.pm b/lib/Template/TT3/Element/Terminator.pm new file mode 100644 index 0000000..8d2d23d --- /dev/null +++ b/lib/Template/TT3/Element/Terminator.pm @@ -0,0 +1,15 @@ +package Template::TT3::Element::Terminator; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element', + view => 'terminator', + alias => { + parse_expr => 'null', + parse_body => 'null', + parse_infix => 'reject', + terminator => 'next_skip_ws', + }; + +1; diff --git a/lib/Template/TT3/Element/Text.pm b/lib/Template/TT3/Element/Text.pm new file mode 100644 index 0000000..e8cf1f4 --- /dev/null +++ b/lib/Template/TT3/Element/Text.pm @@ -0,0 +1,12 @@ +package Template::TT3::Element::Text; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element::Literal', + constant => { + SOURCE_FORMAT => '"%s"', + }; + + +1; \ No newline at end of file diff --git a/lib/Template/TT3/Element/Whitespace.pm b/lib/Template/TT3/Element/Whitespace.pm new file mode 100644 index 0000000..376b4d7 --- /dev/null +++ b/lib/Template/TT3/Element/Whitespace.pm @@ -0,0 +1,41 @@ +package Template::TT3::Element::Whitespace; + +use Template::TT3::Class::Element + version => 2.69, + debug => 0, + base => 'Template::TT3::Element', + view => 'whitespace', + alias => { + skip_ws => 'next_skip_ws', + }; + + +sub skip_delimiter { + # we can always skip whitespace to skip over a delimiter + shift->next_skip_ws( $_[0] ) + ->skip_delimiter( @_ ); +} + + +sub parse_expr { + # we can always skip whitespace to get to an expression + shift->next_skip_ws( $_[0] ) + ->parse_expr( @_ ); +} + + +sub parse_body { + # same for a block + shift->next_skip_ws( $_[0] ) + ->parse_body( @_ ); +} + + +sub parse_pair { + # ditto pair + shift->next_skip_ws( $_[0] ) + ->parse_pair( @_ ); +} + +1; + diff --git a/lib/Template/TT3/Elements.pm b/lib/Template/TT3/Elements.pm index e1c1db9..0a6128e 100644 --- a/lib/Template/TT3/Elements.pm +++ b/lib/Template/TT3/Elements.pm @@ -23,26 +23,26 @@ use Badger::Factory::Class our $PREFIXES = { # short names op_ => 'operator.', + txt_ => 'operator.text.', + num_ => 'operator.number.', + bool_ => 'operator.boolean.', cmd_ => 'command.', ctr_ => 'control.', con_ => 'construct.', - num_ => 'number.', - txt_ => 'text.', sig_ => 'sigil.', var_ => 'variable.', - bool_ => 'boolean.', html_ => 'HTML.', # long names operator_ => 'operator.', + text_ => 'operator.text.', + number_ => 'operator.number.', + boolean_ => 'operator.boolean.', command_ => 'command.', control_ => 'control.', construct_ => 'construct.', - number_ => 'number.', - text_ => 'text.', sigil_ => 'sigil.', variable_ => 'variable.', - boolean_ => 'boolean.', }; diff --git a/lib/Template/TT3/Elements/Core.pm b/lib/Template/TT3/Elements/Core.pm index c9c48b0..933d9bd 100644 --- a/lib/Template/TT3/Elements/Core.pm +++ b/lib/Template/TT3/Elements/Core.pm @@ -1,13 +1,6 @@ use Template::TT3::Elements; -use Template::TT3::Elements::Text; -use Template::TT3::Elements::Number; -use Template::TT3::Elements::Boolean; -use Template::TT3::Elements::Whitespace; -use Template::TT3::Elements::Punctuation; - -#use Template::TT3::Elements::Literal; -#use Template::TT3::Elements::Quotes; -#use Template::TT3::Elements::Variables; -#use Template::TT3::Elements::Operators; +use Template::TT3::Element::Operator::Text; +use Template::TT3::Element::Operator::Number; +use Template::TT3::Element::Operator::Boolean; 1; diff --git a/lib/Template/TT3/Elements/Punctuation.pm b/lib/Template/TT3/Elements/Punctuation.pm deleted file mode 100644 index 741e498..0000000 --- a/lib/Template/TT3/Elements/Punctuation.pm +++ /dev/null @@ -1,207 +0,0 @@ -package Template::TT3::Element::Punctuation; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element', - constants => ':elements'; - - -sub parse_expr { - return undef; -} - -sub OLD_generate { - $_[1]->generate_punctuation( - $_[0]->[TOKEN] - ); -} - - -#----------------------------------------------------------------------- -# TODO: separator -#----------------------------------------------------------------------- - -package Template::TT3::Element::Separator; - -use Template::TT3::Class - version => 3.00, - debug => 0, - base => 'Template::TT3::Element::Punctuation', -# view => 'separator', - constants => ':elements', - constant => { -# is_delimiter => 1, - }; - - -sub skip_separator { - # we can always skip whitespace to skip over a delimiter - $_[0]->next_skip_ws($_[1])->skip_separator($_[1]); -} - -sub skip_delimiter { - # we can always skip whitespace to skip over a delimiter - $_[0]->next_skip_ws($_[1])->skip_delimiter($_[1]); -} - -sub parse_expr { - my ($self, $token, @args) = @_; - $$token = $self->[NEXT] if $token; - $self->[NEXT]->parse_expr($token, @args); -} - - -#----------------------------------------------------------------------- -# statement delimiter: ';' or '%]' or some other tag end -#----------------------------------------------------------------------- - -package Template::TT3::Element::Delimiter; - -use Template::TT3::Class - version => 3.00, - debug => 0, - base => 'Template::TT3::Element::Punctuation', -# as => 'block', - constants => ':elements BLANK', - constant => { - is_delimiter => 1, - FINISH => 'end', - }; - - -sub skip_delimiter { - # we can always skip whitespace to skip over a delimiter - $_[0]->next_skip_ws($_[1])->skip_delimiter($_[1]); -} - - -sub parse_body { - my ($self, $token, $scope, $parent, $follow) = @_; - my (@exprs, $expr, $begfrag, $endfrag, $fragtok); - - $parent ||= $self; - - $self->debug("parse_body(), parent is $parent->[TOKEN]") if DEBUG; - - # skip past token any whitespace, then parse expressions - my $block = $$token->next_skip_ws($token)->parse_block($token, $scope) - || return $parent->fail_missing($self->ARG_BLOCK, $token); - - # if the parent defines any follow-on blocks (e.g. elsif/else for if) - # then we look to see if the next token is one of them and activate it - - # TODO: check that it's safe to assume $follow is always $parent->FOLLOW - # my $follow = $parent->FOLLOW; - - if ($follow && $$token->skip_ws($token)->in($follow)) { - $self->debug("Found follow-on token: ", $$token->token) if DEBUG; - return $$token->parse_follow($block, $token, $scope, $parent); - } - - # otherwise the next token must be our FINISH token (e.g. end) - my $finish = $self->FINISH; - my $endtok = $$token->skip_ws($token); - - return $parent->fail_missing($finish, $token) - unless $$token->is($finish, $token); - - # if a fragment follows then it must match the parent command token - # (e.g. for ... end#for) or the explicit fragment added to the token - # passed to us as $fragment (e.g. for#outer ... end#outer) - if ($fragtok = $$token->parse_fragment($token, $scope)) { - $begfrag = $parent->[FRAGMENT] ? $parent->[FRAGMENT]->[TOKEN] : BLANK; - $endfrag = $fragtok->[TOKEN]; - - $self->debug( - "comparing end fragment: [$endfrag] to parent [$parent->[TOKEN]] [$begfrag]" - ) if DEBUG; - - return ($fragtok || $endtok)->fail_syntax( - bad_fragment => $parent->[TOKEN] . ($begfrag ? '#' . $begfrag : ''), - $endtok->[TOKEN] . '#' . $endfrag - ) - unless $parent->[TOKEN] eq $endfrag - or $begfrag && $begfrag eq $endfrag; - } - - # return the $block we built, not $self which is the delimiter - return $block; -} - - -#----------------------------------------------------------------------- -# Template::TT3::Element::TagEnd - tag end token -#----------------------------------------------------------------------- - -package Template::TT3::Element::TagEnd; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Delimiter', - view => 'tag_end', - constants => ':elements'; - - - -#----------------------------------------------------------------------- -# TODO: terminator -#----------------------------------------------------------------------- - -package Template::TT3::Element::Terminator; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Punctuation', - view => 'terminator', - constants => ':elements', - constant => { - is_terminator => 1, - }, - alias => { - parse_expr => 'null', - parse_body => 'null', - parse_infix => 'reject', - terminator => 'next_skip_ws', - }; - - -package Template::TT3::Element::Fragment; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Terminator', - view => 'fragment', - constants => ':elements'; - - -sub parse_fragment { - my ($self, $token, $scope) = @_; - $$token = $self->[NEXT]; - return $$token->parse_word($token, $scope) - || $self->fail_missing( word => $token ); -} - -#sub parse_expr { -# shift->next_skip_ws($_[0])->parse_expr(@_); -#} - -# TODO: parse_terminator() to terminate and return preceeding block - - - - -#----------------------------------------------------------------------- -# end -#----------------------------------------------------------------------- - -package Template::TT3::Element::End; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Terminator', - view => 'keyword', - constants => ':elements'; - - -1; - diff --git a/lib/Template/TT3/Elements/Structure.pm b/lib/Template/TT3/Elements/Structure.pm deleted file mode 100644 index a221f42..0000000 --- a/lib/Template/TT3/Elements/Structure.pm +++ /dev/null @@ -1,135 +0,0 @@ -# TODO change this to Exprs? - -package Template::TT3::Element::Block; - -die __PACKAGE__, ' is deprecated, use separate module'; - -use Template::TT3::Type::Params 'PARAMS'; -use Template::TT3::Class - version => 3.00, - debug => 0, - base => 'Template::TT3::Element', - view => 'block', - constants => ':elements BLANK', - constant => { - SEXPR_FORMAT => "", - SOURCE_FORMAT => '%s', - SOURCE_JOINT => '; ', - }, - alias => { - exprs => \&expressions, -# value => \&text, -# values => \&text, - }; - - -sub OLD_generate { - $_[CONTEXT]->generate_block( - $_[SELF]->[EXPR], - ); -} - - -sub sexpr { - my $self = shift; - my $format = shift || $self->SEXPR_FORMAT; - my $body = join( - "\n", - map { $_->sexpr } - @{ $self->[EXPR] } - ); - $body =~ s/^/ /gsm if $body; - sprintf( - $format, - $body ? ("\n" . $body . "\n") : '' - ); -} - - -sub source { - my $self = shift; - my $format = shift || $self->SOURCE_FORMAT; - my $joint = shift || $self->SOURCE_JOINT; - sprintf( - $format, - join( - $joint, - map { $_->source } - @{ $self->[EXPR] } - ) - ); -} - - -# TODO: I think value() should return text() - I did it this way to -# avoid the overhead of passing back all items on the stack. - -sub value { - [ - map { - $_[SELF]->debug("calling values() on expr: ", $_->source) if DEBUG; - $_->values($_[CONTEXT]) - } - @{ $_[SELF]->[EXPR] } - ]; -} - - -sub values { - $_[SELF]->debug("called values() on block: ", $_[SELF]->source) if DEBUG; - @{ $_[0]->value($_[1]) } -} - - -sub text { - $_[SELF]->debug("called text() on block: ", $_[SELF]->source) if DEBUG; - join( - BLANK, - grep { defined } # TODO: warn - map { $_->text($_[1]) } - @{ $_[0]->[EXPR] } -# @{ $_[0]->value($_[1]) } - ); -} - - -sub pairs { -# $_[SELF]->debug_caller; - $_[SELF]->debug("called pairs() on block: ", $_[SELF]->source) if DEBUG; - map { $_->pairs($_[CONTEXT]) } - @{ $_[SELF]->[EXPR] } -} - - -sub params { - $_[SELF]->debug("called params() on block: ", $_[SELF]->source) if DEBUG; - - my ($self, $context, $posit, $named) = @_; - $posit ||= [ ]; - $named ||= bless { }, PARAMS; - - $_->params($context, $posit, $named) - for @{ $_[SELF]->[EXPR] }; - - push(@$posit, $named) - if $named && %$named; - - $self->debug("returning ", $self->dump_data($posit)) if DEBUG; - return @$posit; -} - - -sub variable { - # a block of text can be converted to a text variable in order to - # perform dotops on it. - $_[CONTEXT]->{ variables } - ->use_var( $_[SELF], $_[SELF]->text( $_[CONTEXT] ) ); -} - -sub expressions { - return wantarray - ? @{ $_[SELF]->[EXPR] } - : $_[SELF]->[EXPR]; -} - -1; \ No newline at end of file diff --git a/lib/Template/TT3/Elements/Text.pm b/lib/Template/TT3/Elements/Text.pm deleted file mode 100644 index 3fda8cc..0000000 --- a/lib/Template/TT3/Elements/Text.pm +++ /dev/null @@ -1,314 +0,0 @@ -#----------------------------------------------------------------------- -# Template::TT3::Element::Text - base class for literal text elements -#----------------------------------------------------------------------- - -package Template::TT3::Element::Text; - -use Template::TT3::Elements::Operator; -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Literal', - import => 'class', - view => 'text', - constants => ':elements', - constant => { - SEXPR_FORMAT => '', - SOURCE_FORMAT => '"%s"', -# SOURCE_FORMAT => "%s", - }; - - -# TODO: check this isn't being inherited by text ops below... - -sub parse_expr { - my ($self, $token) = @_; - $$token = $self->[NEXT]; # don't use ${$_[1]} - aliasing problem - return $self; -} - - -sub parse_number { - shift->todo; # need to generate numerical assertion op - $_[0]; -} - - -sub sexpr { - sprintf( - $_[0]->SEXPR_FORMAT, - $_[0]->[TOKEN], - ); -} - - -sub source { - # TODO: escape single quotes... - sprintf( - $_[0]->SOURCE_FORMAT, - $_[0]->[TOKEN] - ); -} - - -sub variable { - # text can be converted to a text variable in order to perform dotops on it - $_[CONTEXT]->use_var( - $_[SELF], $_[SELF]->text( $_[CONTEXT] ) - ); -} - - -#----------------------------------------------------------------------- -# Call on generate_text_ops() (in Template::TT3::Class) to create a -# bunch of subclasses of Template::TT3::Element::Text. See the comment -# for generate_number_ops() in Template::TT3::Elements::Number for -# further discussion. For text ops we alias the subroutine specified -# as value(), values() and text(). -#----------------------------------------------------------------------- - -class->generate_text_ops( - convert => prefix => sub { # ~ b - return $_[0]->[EXPR]->text($_[1]) - }, - combine => infix_left => sub { # a ~ b - return $_[0]->[LHS]->text($_[1]) - . $_[0]->[RHS]->text($_[1]) - }, - equal => infix => sub { - return $_[0]->[LHS]->text($_[1]) # a eq b - eq $_[0]->[RHS]->text($_[1]) - }, - not_equal => infix => sub { # a ne b - return $_[0]->[LHS]->text($_[1]) - ne $_[0]->[RHS]->text($_[1]) - }, - less_than => infix => sub { # a lt b - return $_[0]->[LHS]->text($_[1]) - lt $_[0]->[RHS]->text($_[1]) - }, - more_than => infix => sub { # a gt b - return $_[0]->[LHS]->text($_[1]) - gt $_[0]->[RHS]->text($_[1]) - }, - less_equal => infix => sub { # a lt b - return $_[0]->[LHS]->text($_[1]) - le $_[0]->[RHS]->text($_[1]) - }, - more_equal => infix => sub { # a ge b - return $_[0]->[LHS]->text($_[1]) - ge $_[0]->[RHS]->text($_[1]) - }, - compare => infix => sub { # a cmp b - return $_[0]->[LHS]->text($_[1]) - cmp $_[0]->[RHS]->text($_[1]) - }, -); - - -#----------------------------------------------------------------------- -# A call to generate_text_assign_ops() which performs much the same -# task with the exception that it doesn't alias the function to the -# text() method. Instead we inherit the text() method from the -# T::TT3::Element::Operator::Assignment base class which performs the -# assignment (by calling $self->value()) but returns an empty list. -# This is how we silence assignment operators from generating any output -# in "text context", e.g. [% a = 10 %] -#----------------------------------------------------------------------- - -class->generate_text_assign_ops( - combine_set => infix_right => assignment => sub { # a ~= b - return $_[0]->[LHS]->assign( - $_[1], - $_[0]->[LHS]->text($_[1]) - . $_[0]->[RHS]->text($_[1]) - ); - }, -); - - - -#----------------------------------------------------------------------- -# Another call to generate_pre_post_ops() which defines operator classes -# that can be either prefix operators or postfix operators. e.g. '~' -#----------------------------------------------------------------------- - -class->generate_pre_post_ops( - squiggle => ['txt_convert', 'txt_combine'], -); - - - - -#----------------------------------------------------------------------- -# Template::TT3::Element::Padding - a thin subclass of the text element -# used to represent sythesised text tokens added as part of the scanning -# process. For example, the '=' pre and post chomp flags collapse any -# preceding/following text to a single space. We save the original -# whitespace (of which there may be none) as a whitespace token and add -# new padding token of a single space. When we want to re-generate the -# original template source we print out the whitespace but ignore padding. -# OTOH, when we're parsing we ignore whitespace but include padding as -# kind of text expression. -#----------------------------------------------------------------------- - -package Template::TT3::Element::Padding; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Text', - view => 'padding', - constants => ':elements', - constant => { - SEXPR_FORMAT => '', - }; - - - -#----------------------------------------------------------------------- -# Quoted strings. -#----------------------------------------------------------------------- - -package Template::TT3::Element::String; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Text', - view => 'string', - constants => ':elements', - constant => { - SOURCE_FORMAT => '%s', - }, - alias => { - name => \&text, - value => \&text, - values => \&text, - parse_filename => \&parse_expr, - }; - - -sub parse_expr { - my ($self, $token, $scope, $prec) = @_; - - # copy original TEXT into EXPR in case we don't already have a - # reduced form (i.e. without quotes) - $self->[EXPR] = $self->[TOKEN] - unless defined $self->[EXPR]; - - # strings can be followed by postops (postfix and infix operators) - return $$token->next_skip_ws($token) - ->parse_infix($self, $token, $scope, $prec); -} - - -sub filename { - $_[SELF]->[EXPR]; -} - - -sub variable { - $_[CONTEXT]->use_var( - $_[SELF], $_[SELF]->[EXPR] - ); -} - - -sub text { - $_[SELF]->[EXPR]; -} - - -#----------------------------------------------------------------------- -# single quoted strings -#----------------------------------------------------------------------- - -package Template::TT3::Element::Squote; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::String', - view => 'squote', - constants => ':elements', - constant => { - SEXPR_FORMAT => '', - }; - - -sub template { - my $self = shift; - $self->fetch_template($self->[EXPR], @_); -} - - - -#----------------------------------------------------------------------- -# double quoted strings -#----------------------------------------------------------------------- - -package Template::TT3::Element::Dquote; - -use Template::TT3::Class - version => 2.71, - debug => 0, - base => 'Template::TT3::Element::String', - view => 'dquote', - constants => ':elements', - constant => { - SEXPR_FORMAT => '', - }, - alias => { - name => \&text, - value => \&text, - values => \&text, - parse_filename => \&parse_expr, - }; - - -sub parse_expr { - my ($self, $token, $scope) = @_; - my $branch = $self->[BRANCH]; - - $self->advance($token); - - if ($branch) { - $self->[BLOCK] = $branch->parse_block(\$branch, $scope) - || $self->fail_missing( branch => $branch ); - - my $junk = $branch->remaining_text; - return $self->error("Trailing text in double quoted string branch: $junk") - if defined $junk && length $junk; - - $self->debug( - "compiled double quoted string branch: ", - $self->[BLOCK]->source, - ) if DEBUG; - } - - return $$token->skip_ws($token) - ->parse_infix($self, $token, $scope, $self->[META]->[LPREC]); -} - - -sub variable { - $_[CONTEXT]->use_var( - $_[SELF], - $_[SELF]->text( $_[CONTEXT] ) - ); -} - - -sub text { - # If we have a BLOCK then this is a dynamic string, e.g. "foo $bar" - # otherwise it's a static string in EXPR - $_[SELF]->[BLOCK] - ? $_[SELF]->[BLOCK]->text($_[CONTEXT]) - : $_[SELF]->[EXPR] -} - -sub template { - my $self = shift; - return $self->fetch_template( - $self->text(@_), @_ - ); -} - -1; diff --git a/lib/Template/TT3/Elements/Whitespace.pm b/lib/Template/TT3/Elements/Whitespace.pm deleted file mode 100644 index 1a81bd6..0000000 --- a/lib/Template/TT3/Elements/Whitespace.pm +++ /dev/null @@ -1,146 +0,0 @@ -#----------------------------------------------------------------------- -# Template::TT3::Element::Whitespace - literal whitespace elements -#----------------------------------------------------------------------- - -package Template::TT3::Element::Whitespace; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element', - import => 'class', - constants => ':elements', - constant => { - is_whitespace => 1, - }, - alias => { - skip_ws => 'next_skip_ws', - }; - - -sub skip_delimiter { - # we can always skip whitespace to skip over a delimiter - shift->next_skip_ws($_[0])->skip_delimiter(@_); -} - - -sub parse_expr { - # we can always skip whitespace to get to an expression - shift->next_skip_ws($_[0])->parse_expr(@_); -} - - -sub parse_body { - # same for a block - shift->next_skip_ws($_[0])->parse_body(@_); -} - - -sub parse_pair { - # ditto pair - shift->next_skip_ws($_[0])->parse_pair(@_); -} - - -sub generate { - $_[1]->generate_whitespace( - $_[0]->[TOKEN] - ); -} - -sub view { - $_[1]->view_whitespace($_[0]); -} - - - -#----------------------------------------------------------------------- -# Template::TT3::Element::TagStart - tag start token -#----------------------------------------------------------------------- - -package Template::TT3::Element::TagStart; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Whitespace', - constants => ':elements'; - -*skip_ws = \&next_skip_ws; - - -sub next_skip_ws { - # In the case of scan-time control directives (e.g. [? TAGS '<* *>' ?] ) - # we want to hide the tokens inside the directive from the expression - # parser because they have already been interpreted at tokenising time - # and don't equate to runtime expressions that the parser understands. - # So the tokeniser for these tags adds a BRANCH entry in the start token - # for the directive that points to the end token. Whenever we skip_ws - # or next_skip_ws on one of these start tokens (as we always do when - # a whitespace token parse_expr() method is called) then we jump straight - # down to the end token and continue from there. For regular tags, we - # just advance to the next token as usual. - ($_[0]->[BRANCH] && $_[0]->[BRANCH]->skip_ws($_[1])) - || ($_[0]->[NEXT] && $_[0]->[NEXT]->skip_ws($_[1])) - -# my $self = $_[0]; -# $self->debug("[$self] NEXT:[$self->[NEXT]] GOTO:[$self->[GOTO]]"); -# $self->debug("skipping to end of tag [$self->[GOTO]]\n") if $self->[GOTO]; -# $self->debug("tag start next_skip_ws(): ${$_[1]} next is $self->[NEXT] (calling skip_ws())"); - -} - - -sub generate { - $_[1]->generate_tag_start( - $_[0]->[TOKEN] - ); -} - -sub view { - $_[1]->view_tag_start($_[0]); -} - - - - -package Template::TT3::Element::Comment; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element::Whitespace', - constants => ':elements'; - - -sub generate { - $_[1]->generate_comment( - $_[0]->[TOKEN] - ); -} - -sub view { - $_[1]->view_comment($_[0]); -} - - -package Template::TT3::Element::Eof; - -use Template::TT3::Class - version => 3.00, - base => 'Template::TT3::Element', - constants => ':elements', - constant => { - eof => 1, - }, - alias => { - parse_expr => 'null', - }; - -sub generate { - ''; -} - -sub view { - $_[1]->view_eof($_[0]); -} - - -1; diff --git a/lib/Template/TT3/Engines.pm b/lib/Template/TT3/Engines.pm index afccbfd..e68e6b2 100644 --- a/lib/Template/TT3/Engines.pm +++ b/lib/Template/TT3/Engines.pm @@ -1,7 +1,7 @@ package Template::TT3::Engines; -use Template::TT3::Factory::Class - version => 3.00, +use Template::TT3::Class::Factory + version => 2.69, debug => 0, item => 'engine', path => 'Template(X)::(TT3::|)Engine'; @@ -112,6 +112,9 @@ modify it under the same terms as Perl itself. This module inherits methods from the L, L, L, and L base classes. +It is constructed using the L class +metaprogramming module. + It loads modules and instantiates object that are subclasses of L. See L and L for examples. @@ -125,6 +128,3 @@ L for examples. # End: # # vim: expandtab shiftwidth=4: - - - diff --git a/lib/Template/TT3/Exception/Syntax.pm b/lib/Template/TT3/Exception/Syntax.pm index f49730f..2b2ad1b 100644 --- a/lib/Template/TT3/Exception/Syntax.pm +++ b/lib/Template/TT3/Exception/Syntax.pm @@ -8,6 +8,7 @@ use Template::TT3::Class mutators => 'file line position column element token source decorated'; +our $TYPE = 'syntax'; our $FORMAT = 'TT3 syntax error at line of :'; our $ERROR = "\n Error: %s"; our $SOURCE = "\n Source: %s"; diff --git a/lib/Template/TT3/Exceptions.pm b/lib/Template/TT3/Exceptions.pm index 935524c..4709a24 100644 --- a/lib/Template/TT3/Exceptions.pm +++ b/lib/Template/TT3/Exceptions.pm @@ -1,13 +1,115 @@ package Template::TT3::Exceptions; -use Badger::Factory::Class - version => 3.00, +use Template::TT3::Class::Factory + version => 2.69, debug => 0, - item => 'exception_obj', - base => 'Template::TT3::Base', - path => 'Template::TT3::Exception - Template::Exception - TemplateX::TT3::Exception - TemplateX::Exception'; - -1; \ No newline at end of file + item => 'exception_type', + path => 'Template(X)::(TT3::|)Exception'; + + +1; + +__END__ + +=head1 NAME + +Template::TT3::Exceptions - factory module for loading and creating exceptions + +=head1 SYNOPSIS + + use Template::TT3::Exceptions; + + my $factory = Template::TT3::Exceptions->new; + my $error = $factory->item( syntax => @args ); + +=head1 DESCRIPTION + +This module is a subclass of L for locating, loading +and instantiating exception modules used to represent errors. + +It searches for exception modules in the following places: + + Template::TT3::Exception + Template::Exception + TemplateX::TT3::Exception + TemplateX::Exception + +For example, requesting a C exception returns a +L object. Any other arguments +specified are forwarded to the exception constructor method. + + my $error = Template::TT3::Exceptions->item( syntax => @args ); + +=head1 METHODS + +This module inherits all methods from the L, +L, L and L base classes. +The following methods are automatically provided by the L +base class. + +=head2 exception_type($type,@args) / item($type,@args) + +Locates, loads and instantiates an exception module. This is created as an +alias to the L method in L. +Note that we have to use this clumsy name to avoid clashing with the +L method inherited from L. + +=head2 exceptions() + +Method for inspecting or modifying the exceptions that the factory module +manages. This is created as an alias to the L +method in L. + +=head1 PACKAGE VARIABLES + +This module defines the following package variables. These are declarations +that are used by the L base class. + +=head2 $ITEM + +This is the name of the item that the factory module returns, and implicitly +the name of the method by which . In this case it is defined as +C. + +=head2 $PATH + +This defines the module search path for the factory. In this case it is +defined as a list of the following values; + + Template::TT3::Exception + Template::Exception + TemplateX::TT3::Exception + TemplateX::Exception + +=head1 AUTHOR + +Andy Wardley L + +=head1 COPYRIGHT + +Copyright (C) 1996-2009 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. + +=head1 SEE ALSO. + +This module inherits methods from the L, +L, L, and L base classes. + +It is constructed using the L class +metaprogramming module. + +It loads modules and instantiates object that are subclasses of +L. See L and +L for examples. + +=cut + +# Local Variables: +# mode: perl +# perl-indent-level: 4 +# indent-tabs-mode: nil +# End: +# +# vim: expandtab shiftwidth=4: diff --git a/lib/Template/TT3/Factory.pm b/lib/Template/TT3/Factory.pm index 5be6542..5b2da44 100644 --- a/lib/Template/TT3/Factory.pm +++ b/lib/Template/TT3/Factory.pm @@ -1,7 +1,7 @@ package Template::TT3::Factory; use Template::TT3::Class - version => 2.71, + version => 2.69, debug => 0, base => 'Template::TT3::Base Badger::Factory', constants => DEFAULT, @@ -19,6 +19,7 @@ our $DEFAULT = 'TT3'; sub init_factory { my ($self, $config) = @_; # merge all $NAMES definitions into a new 'names' + $self->debug("looking for names in ", join(', ', $self->class->heritage)) if DEBUG; $config->{ names } = $self->class->hash_vars( NAMES => $config->{ names } ); return $self->SUPER::init_factory($config); } @@ -39,7 +40,7 @@ Template::TT3::Factory - base class for factory modules package Template::TT3::Engines; - use Template::TT3::Factory::Class + use Template::TT3::Class::Factory version => 3.00, debug => 0, item => 'engine', @@ -56,7 +57,7 @@ C is a thin subclass of L. It exists to provide a convenient place to define any functionality or declarations that are common to all TT3 factory modules. -The L module is defined as a thin wrapper +The L module is defined as a thin wrapper around L to aid in the construction of factory modules. diff --git a/lib/Template/TT3/Grammar/TT3.pm b/lib/Template/TT3/Grammar/TT3.pm index cb3b3d7..d9c68ff 100644 --- a/lib/Template/TT3/Grammar/TT3.pm +++ b/lib/Template/TT3/Grammar/TT3.pm @@ -30,7 +30,7 @@ our $SYMBOLS = [ [ '-' => num_minus => 275, 285 ], # foo - bar, -foo [ '*' => num_multiply => 280, 0 ], # foo * bar [ '/' => num_divide => 280, 0 ], # foo / bar - [ qr/\s%\s/ => num_percent => 280, 0 ], # foo % bar + [ qr/\s%\s/ => num_modulus => 280, 0 ], # foo % bar [ div => num_div_int => 280, 0 ], # foo div bar [ mod => num_modulus => 280, 0 ], # foo mod bar @@ -66,9 +66,9 @@ our $SYMBOLS = [ # TODO: '!!' as a prefix operator: !!some_var_that_may_be_undefined [ '!!' => bool_nor => 250, 0 ], # foo !! bar - [ '..' => num_range => 240, 0 ], # 1 .. 91 - [ 'to' => num_to => 240, 0 ], # 1 to 91 by 10 # TODO: by - [ 'by' => num_by => 240, 0 ], # 1 to 91 by 10 # TODO: by + [ '..' => op_range => 240, 0 ], # 1 .. 91 + [ 'to' => op_range => 240, 0 ], # 1 to 91 by 10 # TODO: to + [ 'by' => op_step => 240, 0 ], # 1 to 91 by 10 # TODO: by [ '?' => op_ternary => 230, 0 ], # foo ? bar diff --git a/lib/Template/TT3/Modules.pm b/lib/Template/TT3/Modules.pm index bfe53b4..adac3c9 100644 --- a/lib/Template/TT3/Modules.pm +++ b/lib/Template/TT3/Modules.pm @@ -8,38 +8,39 @@ use Template::TT3::Class FILESYSTEM_MODULE => 'Badger::Filesystem', # TT3 modules - CACHE_MODULE => 'Template::TT3::Cache', - CONTEXT_MODULE => 'Template::TT3::Context', - SERVICES_MODULE => 'Template::TT3::Services', - DIALECT_MODULE => 'Template::TT3::Dialect', - DIALECTS_MODULE => 'Template::TT3::Dialects', - ELEMENT_MODULE => 'Template::TT3::Element', - ENGINES_MODULE => 'Template::TT3::Engines', - EXCEPTIONS_MODULE => 'Template::TT3::Exceptions', - HUB_MODULE => 'Template::TT3::Hub', - ITERATOR_MODULE => 'Template::TT3::Iterator', - PLUGINS_MODULE => 'Template::TT3::Plugins', - PROVIDERS_MODULE => 'Template::TT3::Providers', - SERVICE_MODULE => 'Template::TT3::Service', - SCANNER_MODULE => 'Template::TT3::Scanner', - SITEMAP_MODULE => 'Template::TT3::Site::Map', - SITEMAPS_MODULE => 'Template::TT3::Site::Maps', - STORE_MODULE => 'Template::TT3::Store', - TAG_MODULE => 'Template::TT3::Tag', - TAGSET_MODULE => 'Template::TT3::Tagset', - TEMPLATE_MODULE => 'Template::TT3::Template', - TEMPLATES_MODULE => 'Template::TT3::Templates', - VIEWS_MODULE => 'Template::TT3::Views', + CACHE_MODULE => 'Template::TT3::Cache', + CONTEXT_MODULE => 'Template::TT3::Context', + SERVICES_MODULE => 'Template::TT3::Services', + DIALECT_MODULE => 'Template::TT3::Dialect', + DIALECTS_MODULE => 'Template::TT3::Dialects', + ELEMENT_MODULE => 'Template::TT3::Element', + ELEMENT_ROLE_MODULE => 'Template::TT3::Element::Role', + ENGINES_MODULE => 'Template::TT3::Engines', + EXCEPTIONS_MODULE => 'Template::TT3::Exceptions', + HUB_MODULE => 'Template::TT3::Hub', + ITERATOR_MODULE => 'Template::TT3::Iterator', + PLUGINS_MODULE => 'Template::TT3::Plugins', + PROVIDERS_MODULE => 'Template::TT3::Providers', + SERVICE_MODULE => 'Template::TT3::Service', + SCANNER_MODULE => 'Template::TT3::Scanner', + SITEMAP_MODULE => 'Template::TT3::Site::Map', + SITEMAPS_MODULE => 'Template::TT3::Site::Maps', + STORE_MODULE => 'Template::TT3::Store', + TAG_MODULE => 'Template::TT3::Tag', + TAGSET_MODULE => 'Template::TT3::Tagset', + TEMPLATE_MODULE => 'Template::TT3::Template', + TEMPLATES_MODULE => 'Template::TT3::Templates', + VIEWS_MODULE => 'Template::TT3::Views', # other modules - IO_HANDLE => 'IO::Handle', - CODECS_MODULE => 'Badger::Codecs', + IO_HANDLE => 'IO::Handle', + CODECS_MODULE => 'Badger::Codecs', }, exports => { any => 'CONTEXT_MODULE HUB_MODULE EXCEPTIONS_MODULE TEMPLATE_MODULE DIALECT_MODULE SCANNER_MODULE DIALECT_CLASS TAG_MODULE TAGSET_MODULE ENGINES_MODULE VIEWS_MODULE - ELEMENT_MODULE IO_HANDLE SERVICE_MODULE ITERATOR_MODULE', + ELEMENT_MODULE ELEMENT_ROLE_MODULE IO_HANDLE SERVICE_MODULE ITERATOR_MODULE', tags => { hub => 'FILESYSTEM_MODULE DIALECTS_MODULE TEMPLATES_MODULE CACHE_MODULE STORE_MODULE PROVIDERS_MODULE PLUGINS_MODULE diff --git a/lib/Template/TT3/Providers.pm b/lib/Template/TT3/Providers.pm index 674873e..497251a 100644 --- a/lib/Template/TT3/Providers.pm +++ b/lib/Template/TT3/Providers.pm @@ -1,7 +1,7 @@ package Template::TT3::Providers; -use Template::TT3::Factory::Class - version => 3.00, +use Template::TT3::Class::Factory + version => 2.69, debug => 0, item => 'provider', path => 'Template(X)::(TT3::|)Provider', @@ -118,6 +118,9 @@ modify it under the same terms as Perl itself. This module inherits methods from the L, L, L, and L base classes. +It is constructed using the L class +metaprogramming module. + It loads modules and instantiates object that are subclasses of L. See L and L for examples of specific provider modules. diff --git a/lib/Template/TT3/Tag/Control.pm b/lib/Template/TT3/Tag/Control.pm index 8971900..9372923 100644 --- a/lib/Template/TT3/Tag/Control.pm +++ b/lib/Template/TT3/Tag/Control.pm @@ -1,7 +1,6 @@ package Template::TT3::Tag::Control; use Template::TT3::Grammar::Control; -use Template::TT3::Elements::Punctuation; use Template::TT3::Class version => 2.71, debug => 0, diff --git a/lib/Template/TT3/Types.pm b/lib/Template/TT3/Types.pm index af34781..5528054 100644 --- a/lib/Template/TT3/Types.pm +++ b/lib/Template/TT3/Types.pm @@ -1,63 +1,29 @@ -#======================================================================== -# -# Template::TT3::Types -# -# DESCRIPTION -# Factory module for loading and instantiating Template::TT3::Type objects -# on demand. -# -# AUTHOR -# Andy Wardley -# -#======================================================================== - package Template::TT3::Types; -use Badger::Factory::Class - version => 3.00, +use Template::TT3::Class::Factory + version => 2.69, debug => 0, item => 'type', - base => 'Template::TT3::Base', - path => 'Template::TT3::Type', - types => { - # Perl's names, and our made-up names to map different data types - # to method providers - UNDEF => 'Template::TT3::Type::Undef', - TEXT => 'Template::TT3::Type::Text', - ARRAY => 'Template::TT3::Type::List', - HASH => 'Template::TT3::Type::Hash', - CODE => 'Template::TT3::Type::Code', -# OBJECT => 'Template::TT3::Type::Object', - - # lower case TT names -# text => 'Template::TT3::Type::Text', -# list => 'Template::TT3::Type::List', -# hash => 'Template::TT3::Type::Hash', -# params => 'Template::TT3::Type::Params', + path => 'Template(X)::(TT3::|)Type', + names => { + TEXT => 'text', + CODE => 'code', + ARRAY => 'list', + HASH => 'hash', + UNDEF => 'undef', }; -our $STANDARD_TYPES = [qw( UNDEF VALUE ARRAY HASH CODE OBJECT )]; +our @STANDARD_TYPES = qw( TEXT CODE ARRAY HASH CODE UNDEF ); our $VTABLES = { }; -sub OLD_init { - my ($self, $config) = @_; -# $self->{ types } = $self->class->hash_vars( TYPES => $config->{ types } ); - $self->init_factory($config); - return $self; -} - -sub TMP_init { - my ($self, $config) = @_; - $self->init_factory($config); -# $self->debug("types: ", $self->dump_data($self->{ types })); - return $self; -} - sub preload { my $self = shift->prototype; my $types = $self->types; my $loads = { }; + + # all change + $types = $TYPE_NAMES; $self->debug("preload() types: ", $self->dump_data($types)) if DEBUG; diff --git a/t/class/element.t b/t/class/element.t new file mode 100644 index 0000000..17c2830 --- /dev/null +++ b/t/class/element.t @@ -0,0 +1,45 @@ +#============================================================= -*-perl-*- +# +# t/class/element.t +# +# Test the Template::TT3::Class::Element metaprogramming module +# for element class construction. +# +# Run with the -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Class::Element', + args => \@ARGV, + tests => 3; + +use Template::TT3::Class::Element; +use constant CLASS => 'Template::TT3::Class::Element'; +pass( 'loaded ' . CLASS ); + + +package Template::TT3::Element::Test::Foo; +use Template::TT3::Class::Element; + +package main; +my $foo = Template::TT3::Element::Test::Foo->new; +is( $foo->type, 'test_foo', 'element has default type' ); + + +package Template::TT3::Element::Test::Bar; +use Template::TT3::Class::Element + type => 'barbar'; + +package main; +my $bar = Template::TT3::Element::Test::Bar->new; +is( $bar->type, 'barbar', 'element has custom type' ); + diff --git a/t/class/factory.t b/t/class/factory.t new file mode 100644 index 0000000..3e28686 --- /dev/null +++ b/t/class/factory.t @@ -0,0 +1,45 @@ +#============================================================= -*-perl-*- +# +# t/class/factory.t +# +# Test the Template::TT3::Class::Factory metaprogramming module +# for factory class construction. +# +# Run with the -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Class::Factory', + args => \@ARGV, + tests => 5; + +use Template::TT3::Class::Factory; +use constant CLASS => 'Template::TT3::Class::Factory'; +pass( 'loaded ' . CLASS ); + + +package Template::TT3::Test::Factory; + +use Template::TT3::Class::Factory + item => 'widget', + path => 'Template(X)::(TT3::|)Widget'; + +package main; + +my $factory = Template::TT3::Test::Factory->new; +ok( $factory, 'created widget factory' ); + +my $path = $factory->path; +ok( $path, 'got factory path' ); +is( scalar(@$path), 4, 'four items in path' ); +is( $path->[0], 'Template::TT3::Widget', 'first path item is correct' ); + diff --git a/t/dialect/dialects.t b/t/dialect/dialects.t index 199577e..e663715 100644 --- a/t/dialect/dialects.t +++ b/t/dialect/dialects.t @@ -2,9 +2,10 @@ # # t/dialect/dialects.t # -# Test the Template::TT3::Dialects module. +# Test the Template::TT3::Dialects module, including the ability to +# create a custom dialect. # -# Run with -h option for help. +# Run with the -h option for help. # # Written by Andy Wardley # @@ -13,38 +14,60 @@ # #======================================================================== -use lib - '/home/abw/projects/badger/lib'; # testing Badger changes - - use Badger - lib => '../../lib'; + lib => '../../lib'; use Template::TT3::Test - debug => 'Template::TT3::Dialects', - args => \@ARGV, - tests => 7; + debug => 'Template::TT3::Dialects Template::TT3::Dialect', + args => \@ARGV, + tests => 12; + -use constant - DIALECTS => 'Template::TT3::Dialects'; +#----------------------------------------------------------------------- +# basic tests +#----------------------------------------------------------------------- use Template::TT3::Dialects; -pass( 'loaded Template::TT3::Dialects' ); +use constant DIALECTS => 'Template::TT3::Dialects'; +pass('loaded Template::TT3::Dialects' ); + +my $tt3 = DIALECTS->dialect('tt3'); +ok( $tt3, 'got tt3 dialect from class' ); + +is( $tt3->name, 'tt3', 'got dialect name' ); +is( "$tt3", 'tt3', 'got dialect name by auto-stringification' ); + +my $tagset = $tt3->tagset; +ok( $tagset, 'got tagset' ); + + +#----------------------------------------------------------------------- +# customising an existing dialect +#----------------------------------------------------------------------- + +my $dialects = DIALECTS->new( + dialects => { + tt3 => { + tags => '<* *>', + } + } +); +ok( $dialects, 'created dialects object'); -# get default dialect -my $dialect = DIALECTS->dialect; -ok( $dialect, 'got default dialect' ); -is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got default TT3 dialect' ); +$tt3 = $dialects->dialect('tt3'); +ok( $tt3, 'got tt3 dialect from object' ); -# get specific dialect -$dialect = DIALECTS->dialect('tt3'); -ok( $dialect, 'got tt3 dialect' ); -is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got tt3 dialect module' ); +$tagset = $tt3->tagset; +#print $tagset->dump; -# in capitals this time -$dialect = DIALECTS->dialect('TT3'); -ok( $dialect, 'got TT3 dialect' ); -is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got TT3 dialect module' ); +my $scanner = $tt3->scanner; +ok( $scanner, 'got scanner' ); +$tagset = $scanner->tagset; +ok( $tagset, 'got scanner tagset' ); +my $inline = $tagset->tag('inline'); +ok( $inline, 'got inline tag' ); +is( $inline->start, '<*', 'inline start is set to <*' ); +is( $inline->end, '*>', 'inline end is set to *>' ); diff --git a/t/element/literal.t b/t/element/literal.t new file mode 100644 index 0000000..19589b2 --- /dev/null +++ b/t/element/literal.t @@ -0,0 +1,42 @@ +#============================================================= -*-perl-*- +# +# t/element/literal.t +# +# Test the Template::TT3::Element::Literal module. +# +# Run with the -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Element::Literal', + args => \@ARGV, + tests => 7; + +use Template::TT3::Element::Literal; +use Template::TT3::Elements; +use constant { + ELEMENT => 'Template::TT3::Element::Literal', + ELEMENTS => 'Template::TT3::Elements', +}; + +pass( 'loaded ' . ELEMENT ); + +my $literal = ELEMENT->new(undef, undef, 'hello'); +ok( $literal, 'created a literal element' ); + +is( $literal->text, 'hello', 'got literal text()'); +is( $literal->value, 'hello', 'got literal value()'); +is( $literal->values, 'hello', 'got literal values()'); + +$literal = ELEMENTS->prototype->construct( literal => 'world' ); +ok( $literal, 'constructed literal element via element factory' ); +is( $literal->text, 'world', 'got literal text(): world'); diff --git a/t/element/text.t b/t/element/text.t new file mode 100644 index 0000000..3cba9fc --- /dev/null +++ b/t/element/text.t @@ -0,0 +1,42 @@ +#============================================================= -*-perl-*- +# +# t/element/text.t +# +# Test the Template::TT3::Element::Text module. +# +# Run with the -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Element::Text', + args => \@ARGV, + tests => 7; + +use Template::TT3::Elements; +use Template::TT3::Element::Text; +use constant { + ELEMENT => 'Template::TT3::Element::Text', + ELEMENTS => 'Template::TT3::Elements', +}; + +pass( 'loaded ' . ELEMENT ); + +my $text = ELEMENT->new(undef, undef, 'hello'); +ok( $text, 'created a text element' ); + +is( $text->text, 'hello', 'got text text()'); +is( $text->value, 'hello', 'got text value()'); +is( $text->values, 'hello', 'got text values()'); + +$text = ELEMENTS->prototype->construct( text => 'world' ); +ok( $text, 'constructed text element via element factory' ); +is( $text->text, 'world', 'got text: world'); diff --git a/t/factory/dialects.t b/t/factory/dialects.t new file mode 100644 index 0000000..7b1c9cf --- /dev/null +++ b/t/factory/dialects.t @@ -0,0 +1,45 @@ +#============================================================= -*-perl-*- +# +# t/factory/dialects.t +# +# Test the Template::TT3::Dialects factory module which loads and +# instantiates dialect objects. +# +# Run with -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Dialects', + args => \@ARGV, + tests => 7; + +use constant + DIALECTS => 'Template::TT3::Dialects'; + +use Template::TT3::Dialects; +pass( 'loaded Template::TT3::Dialects' ); + +# get default dialect +my $dialect = DIALECTS->dialect; +ok( $dialect, 'got default dialect' ); +is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got default TT3 dialect' ); + +# get specific dialect +$dialect = DIALECTS->dialect('tt3'); +ok( $dialect, 'got tt3 dialect' ); +is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got tt3 dialect module' ); + +# in capitals this time +$dialect = DIALECTS->dialect('TT3'); +ok( $dialect, 'got TT3 dialect' ); +is( ref $dialect, 'Template::TT3::Dialect::TT3', 'got TT3 dialect module' ); + diff --git a/t/engine/engines.t b/t/factory/engines.t similarity index 87% rename from t/engine/engines.t rename to t/factory/engines.t index f7197af..a197440 100644 --- a/t/engine/engines.t +++ b/t/factory/engines.t @@ -1,8 +1,9 @@ #============================================================= -*-perl-*- # -# t/engine/engines.t +# t/factory/engines.t # -# Test the Template::TT3::Engines module. +# Test the Template::TT3::Engines factory module which loads and +# instantiates engine modules. # # Run with -h option for help. # @@ -13,10 +14,6 @@ # #======================================================================== -use lib - '/home/abw/projects/badger/lib'; # testing Badger changes - - use Badger lib => '../../lib'; @@ -46,5 +43,3 @@ $engine = ENGINES->engine('TT3'); ok( $engine, 'got TT3 engine' ); is( ref $engine, 'Template::TT3::Engine::TT3', 'got TT3 engine module' ); - - diff --git a/t/factory/exceptions.t b/t/factory/exceptions.t new file mode 100644 index 0000000..97924ac --- /dev/null +++ b/t/factory/exceptions.t @@ -0,0 +1,35 @@ +#============================================================= -*-perl-*- +# +# t/factory/exceptions.t +# +# Test the Template::TT3::Exceptions factory module which loads and +# instantiates exception objects used to represent errors. +# +# Run with -h option for help. +# +# Written by Andy Wardley +# +# This is free software; you can redistribute it and/or modify it +# under the same terms as Perl itself. +# +#======================================================================== + +use Badger + lib => '../../lib'; + +use Template::TT3::Test + debug => 'Template::TT3::Exceptions', + args => \@ARGV, + tests => 4; + +use constant + FACTORY => 'Template::TT3::Exceptions'; + +use Template::TT3::Exceptions; +pass( 'loaded ' . FACTORY ); + +my $syntax = FACTORY->item( syntax => { info => 'just testing' } ); +ok( $syntax, 'got syntax error' ); +is( $syntax->type, 'syntax', 'got syntax error type' ); +is( $syntax->info, 'just testing', 'got syntax error info' ); + diff --git a/t/provider/providers.t b/t/factory/providers.t similarity index 88% rename from t/provider/providers.t rename to t/factory/providers.t index eaf3736..44b7a43 100644 --- a/t/provider/providers.t +++ b/t/factory/providers.t @@ -1,8 +1,9 @@ #============================================================= -*-perl-*- # -# t/provider/providers.t +# t/factory/providers.t # -# Test the Template::TT3::Providers module. +# Test the Template::TT3::Providers factory module which loads and +# instantiates provider modules. # # Run with -h option for help. # @@ -13,9 +14,6 @@ # #======================================================================== -use lib - '/home/abw/projects/badger/lib'; # testing Badger changes - use Badger lib => '../../lib', Filesystem => 'Bin'; diff --git a/t/modules/types.t b/t/factory/types.t similarity index 87% rename from t/modules/types.t rename to t/factory/types.t index 4d365da..d77dea0 100644 --- a/t/modules/types.t +++ b/t/factory/types.t @@ -1,8 +1,12 @@ #============================================================= -*-perl-*- # -# t/modules/types.t +# t/factory/types.t # -# Test the Template::TT3::Types module. +# Test the Template::TT3::Types factory module responsible for loading +# and instantiate data type modules (text, list, hash, etc) that +# implement the virtual data methods. +# +# Run with the -h option for help. # # Written by Andy Wardley # @@ -11,15 +15,17 @@ # #======================================================================== -#use Badger::Debug modules => 'Badger::Factory'; -use Badger lib => '../../lib'; +use Badger + lib => '../../lib'; + use Template::TT3::Test debug => 'Badger::Factory Template::TT3::Types', args => \@ARGV, - tests => 43; + tests => 48; use Template::TT3::Types; use constant TYPES => 'Template::TT3::Types'; +use Badger::Debug ':all'; ok( TYPES->preload, 'preload' ); @@ -75,8 +81,19 @@ is( $hash->item('foo'), 20, 'foo item is 20' ); is( $hash->{ bar }, 30, 'bar item is 30' ); +#----------------------------------------------------------------------- +# should be able to use aliases for all those +#----------------------------------------------------------------------- + +ok( TYPES->type('UNDEF'), 'got UNDEF type' ); +ok( TYPES->type('TEXT'), 'got TEXT type' ); +ok( TYPES->type('CODE'), 'got CODE type' ); +ok( TYPES->type('HASH'), 'got HASH type' ); +ok( TYPES->type('ARRAY'), 'got ARRAY type' ); + + #------------------------------------------------------------------------ -# try bad object name +# try bad type name #------------------------------------------------------------------------ ok( ! TYPES->try->create( frobulator => 99 ), 'no frobulator' ); @@ -97,7 +114,6 @@ $list = $types->create( list => [ 'Hello', 'World'] ); is( $list->first, 'Hello', 'got object list first' ); is( $list->[-1], 'World', 'got object list last' ); - $hash = $types->create( hash => { 'Hello' => 'World' } ); is( $hash->item('Hello'), 'World', 'got object hash item' ); is( $hash->{'Hello'}, 'World', 'got object hash item direct' ); @@ -124,7 +140,7 @@ is( $types->error->info, "type not found: hash", 'no hash error' ); #----------------------------------------------------------------------- -# get the vtables info +# get the vtables info - NOTE: this is going to be moved out #----------------------------------------------------------------------- my $vtables = TYPES->vtables; diff --git a/t/modules/dialects.t b/t/modules/dialects.t deleted file mode 100644 index 2da5165..0000000 --- a/t/modules/dialects.t +++ /dev/null @@ -1,69 +0,0 @@ -#============================================================= -*-perl-*- -# -# t/modules/dialects.t -# -# Test the Template::TT3::Dialects module. -# -# Written by Andy Wardley -# -# This is free software; you can redistribute it and/or modify it -# under the same terms as Perl itself. -# -#======================================================================== - -use Badger - lib => '../../lib'; - -use Template::TT3::Test - debug => 'Template::TT3::Dialects Template::TT3::Dialect', - args => \@ARGV, - tests => 12; - - -#----------------------------------------------------------------------- -# basic tests -#----------------------------------------------------------------------- - -use Template::TT3::Dialects; -use constant DIALECTS => 'Template::TT3::Dialects'; -pass('loaded Template::TT3::Dialects' ); - -my $tt3 = DIALECTS->dialect('tt3'); -ok( $tt3, 'got tt3 dialect from class' ); - -is( $tt3->name, 'tt3', 'got dialect name' ); -is( "$tt3", 'tt3', 'got dialect name by auto-stringification' ); - -my $tagset = $tt3->tagset; -ok( $tagset, 'got tagset' ); - - -#----------------------------------------------------------------------- -# customising an existing dialect -#----------------------------------------------------------------------- - -my $dialects = DIALECTS->new( - dialects => { - tt3 => { - tags => '<* *>', - } - } -); -ok( $dialects, 'created dialects object'); - -$tt3 = $dialects->dialect('tt3'); -ok( $tt3, 'got tt3 dialect from object' ); - -$tagset = $tt3->tagset; -#print $tagset->dump; - -my $scanner = $tt3->scanner; -ok( $scanner, 'got scanner' ); - -$tagset = $scanner->tagset; -ok( $tagset, 'got scanner tagset' ); - -my $inline = $tagset->tag('inline'); -ok( $inline, 'got inline tag' ); -is( $inline->start, '<*', 'inline start is set to <*' ); -is( $inline->end, '*>', 'inline end is set to *>' );