################################# # classes (packages) involved ################################# # # main package # (at least that's where everything starts ;) # package Template::Parser; ### # CLASS: TAG ### # # Implements abstract class for real # tag sub-classes (such as TAG::Output and etc.) # package Template::Parser::TAG; ### # CLASS: TAG::Output ### # # package Template::Parser::TAG::Output; ### # CLASS: TAG::Control ### # # base class for various control tags. # # e.g. , etc. package Template::Parser::TAG::Control; ### # CLASS: TAG::Control::If ### # # encapsulates conditional tags (e.g. in ColdFusion) # package Template::Parser::TAG::Control::If; ### # CLASS: TAG::Control::VAR ### # # base class for various variables. # package Template::Parse::VAR; ### # CLASS: TAG::Control::VAR::Simple ### # # simple variable (err. pretty much any # Perl non-object variable will do) # package Template::Parse::VAR::Simple; ### # CLASS: TAG::Control::VAR::Query ### # # special query object # (think of ColdFusion queries ;) # package Template::Parse::VAR::Query ### # CLASS: EXPR ### # # class handling complex expressions # # EXAMPLE: # # template = # # # # will force Parser to create # an EXPR object from "bar * 2". # An EXPR object should than be # able to parse the expression. # # There should be a method for an EXPR # object to query a variable. # # I think this could be achieved # by having the Parser object pass # a reference to itself so that # the newly created EXPR object # can query for any variable value # as needed (say, when it's turn to # process the expression has come) # # This may also allow for 'propagated' # query whereby a value for the # first occurance of a variable # is returned.. which in turn # will create nice variable scoping. # package Template::Parse::EXPR # # Other HTML text. # note: might not be needed as my tag # stack will hold 'scalar' refs for # plain text that has nothing to parse in it. package Template::Parse::TEXT; 1; __END__ =head1 TECHNICAL NOTES =head2 PARSING Implemented by _parse() Algorithm: Sample template: Hello World, #name#! foobar! 1. build tag stack 1.1 split template by tag separators (default '<') E.g.: will get 0 1 Hello World 2 #name# 3 ! 4 foobar! 5 6 7 1.2 For each recognizable tag, build a new object using tag/object mappings. For example like this (largly simpliefied version): %tag_mapping = ( 'cfoutput' => 'Template::Parse::TAG::output', ); Therefore, for the first tag (at 2) a Template::Parse::TAG::Output object will be instantiated. Now, I'm wondering how to do this..... Here's one thought: 1. the main Parser object is responsible for finding a pair of tags and instantiating a new TAG object by passing whatever belongs inside the pair of tags. In that sense, a TAG::output object will be created by passing this text to it (and nothing else): "#name#" The new tag object's task will then rest in dealing with parsing this little piece of text. For example, it may instantiate yet another Parser object passing it the string '#name#' (or whatever lies inside the pair of tags). The parser will than notice that the text it has received contains a single variable and create a new VAR object. The tag object will control the parser object to do one thing or another. Also, since certain tags may not contain other tags inside them (such as , which can't hold another ... whereas, may be nested). This could be assured within the parser by checking tag's nested property. This could be accomplished by coding TAG::Output class to instantiate its objects with the 'nested' attribute set to 0 by default. Remember that TAG::Output has Template::TAG as it's parent and, therefore, the nested property may be defined in that base class as a way of assuring that child objects contain this attribute. =head1 CONTROL FLOW It's rather simple for a template. With the exception of various control structures such as loops/switches etc a template is parsed top-down. Appropriate loop/control tag objects will handle the rest. As for the basic top-down approach, the main Parser object should maintain a proper sequence/ordering of tags that it encounters during parsing. Since examples work the best, let me bring up a few: EXAMPLE 1: Consider a template: ------------------- Hello World, #name#! foobar! foo... ------------------- The Parent instance of Parser will have knowledge of the following tags that are marked with '*' (note that the topmost Parser object knows nothing of individual tag objects' contents) -*- 1 Parse::Text -- Hello World, -*- 2 TAGS::Output -- '#name#' -*- 3 TAGS::Control::If -- CONDITION: 1 -- Parse::EXPR -- 'foo' 2 'eq' 3 -- Parse::VAR -- 1 BLOCK_TRUE: -- Parse::Control::If -- CONDITION: 1 -- Parse::EXPR -- 'bar' 2 'eq' 3 -- Parse::VAR -- 1 BLOCK_TRUE: -- Parser -- -- Parser::Text -- 'foobar!' BLOCK_FALSE: -- Parser -- -- Parser::Text -- 'foo...' BLOCK_FALSE: nil -*- 4 Parse::Text -- ----------------------------- With such structure in place (which is a major piece of work in the entire parsing process), it should be possible to construct final HTML document by starting at the first tag and keep transferring 'control' all the way to the bottom tag object. =head2 VARIABLE SCOPES Read notes under Template::Parse::EXPR. Referring to previous tag stack diagram, allowing various blocks of Template 'code' to be nested (through the use of nested tags for example) serves us good in that variable scoping may now be easily achieved by doing successive queries up the stack tree and returning the first value thus found. ...