Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Templating suggestions for code generation

by bart (Canon)
on Feb 17, 2005 at 00:30 UTC ( [id://431797]=perlquestion: print w/replies, xml ) Need Help??

bart has asked for the wisdom of the Perl Monks concerning the following question:

I am contemplating building a code generator system, but I'm not sure on how to implement the code templates. I am preoccupied with simplicity of the syntax for the templates themselves (not necessarily for the associated perl code), to achieve some typical tasks.

So, this is a bit of a contest for templating systems, I am curious how you would implement one particular example problem, in your favourite templating system. The example problem is this: starting with a Perl array, it should produce the following output:

empty array: nothing is inserted
// BEGIN // END
one or more items: a global block is inserted (in this example, a switch statement, with braces), and for each item a subblock is inserted inside that global block (the individual cases). With the array = ("foo", "bar", "baz"), the desired output is:
// BEGIN switch(x) { case foo: // code for foo break; case bar: // code for bar break; case baz: // code for baz } // END

I'd like to point out one particularity, which often appears to be a weak spot. Note that there is no "break;" line after the last item in the list. The idea is that some particular text should be inserted between items in a loop, but not after the last item. It should act like the join string in join. As another example, it should be possible to expand a Perl array ("foo", "bar", "baz") into the text

(foo, bar, baz)
Thus, with commas between the items, but not at the end.

Again: at this point, I'm only interested in the templates themselves, and not yet in the Perl code to drive it.

To show what I am after, it could be something like the following, fictional example:

// BEGIN <%IF @LIST%> switch(x) { <%FOREACH @LIST AS ITEM%> case $ITEM: // code for $ITEM <%CONTINUE%> break; <%/FOREACH%> } <%/IF%> // END

So, if you know of a templating system that can do something like this: great! If you know of something better still... even better! :)

Replies are listed 'Best First'.
Re: Templating suggestions for code generation
by merlyn (Sage) on Feb 17, 2005 at 00:50 UTC
Re: Templating suggestions for code generation
by BrowserUk (Patriarch) on Feb 17, 2005 at 01:29 UTC

    I may have complety missed the mark, but have you looked at the Text::Template module?

    In particular, what your decribing made me think of the Philosophy section of Dominus' module.

    Of the three templating modules I played with, the other two being HTML::Template, Template::Toolkit, it had the smallest learning curve and the (to my mind) greatest flexibility. Worth a look if you haven't already.


    Examine what is said, not who speaks.
    Silence betokens consent.
    Love the truth but pardon error.
      I work with an application that uses Text::Template and, having worked with the others as well, I must say that Text::Template is the worst templating module I've ever used. There is a ton of business code within the templates, logic that is just abysmal, and no separation of concerns.

      While I understand the drive that Dominus had to build Text::Template, the amount of self-discipline required to successfully use it boggles the mind, especially when alternatives that help provide that discipline for you exist. I simply cannot recommend its usage in any application.

      Being right, does not endow the right to be rude; politeness costs nothing.
      Being unknowing, is not the same as being stupid.
      Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence.
      Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.

        I think I might agree with you if your application is a web application where the drive to keep business logic out of the html is a priority, but reading Bart's OP, that did not seem to be the case here.


        Examine what is said, not who speaks.
        Silence betokens consent.
        Love the truth but pardon error.
Re: Templating suggestions for code generation
by lachoy (Parson) on Feb 17, 2005 at 01:50 UTC

    I gave a presentation at YAPC 2003 about generating Java with Perl. I strongly agree with Randal regarding the Template Toolkit, although even something like Text::Template would be good too.

    While the presentation doesn't have a ton of Template Toolkit examples you might find it useful -- download the tarball and see for yourself. (The TT examples are 'extra' slides toward the end.)

    Chris
    M-x auto-bs-mode

Re: Templating suggestions for code generation
by sh1tn (Priest) on Feb 17, 2005 at 01:59 UTC
    As far as I understand the problem -
    (from template toolkit)
    [% FOREACH item IN items %] [% IF loop.first %] <ul> [% END %] <li>[% item %] ([% loop.count %] of [% loop.size %])</li> [% IF loop.last %] </ul> [% END %] [% END %]


Re: Templating suggestions for code generation
by dimar (Curate) on Feb 17, 2005 at 02:00 UTC

    ... and of course, for the fans of that curious and little-known templating language "perl" ... (cheatsheet version, "readmore" section has the full version)

    ### -------------------------- .q© // BEGIN© .sub{(@{$oDt})?" switch(x){" .sLp($oDt,sub{my $sBrk=($iCt++<$#{$oDt})?'break;':'';" case $_: // code for $_ $sBrk" })." }" :''}->(). q© // END ©; ### --------------------------
Re: Templating suggestions for code generation
by cbrandtbuffalo (Deacon) on Feb 17, 2005 at 13:14 UTC
    I agree with the suggestion to use Template::Toolkit. I have worked with merlyn on using Template Toolkit to generate httpd.conf files for a series of servers (dev configuration, then qa, then prod). This allows us to leave key directives the same and test them through our environment and be sure they will work in produciton. So it works well for all types of templating.

    Your example would look something like:

    [%- items = ['one', 'two', 'three'] -%] # Output an array data structure in perl. @array = ( [%- FOREACH item IN items -%] [%- IF loop.last -%] '[%- item -%]' [%- ELSE -%] '[%- item -%]', [%- END -%] [%- END -%]);

    You can run this from the command line after you install Template using the tpage command. The extra '-' gets rid of whitespace from inside the template directives. If you want the code to look nice, you'll have to play with the spacing a bit.

      Thank you. I don't know how I missed the "loop.last" property for the FOREACH directive, I clearly was looking at the wrong spot in the docs. It obviously is the right property to use to put text between items.

      So, to answer my own question, for Template::Toolkit, this is a good way to do what I wanted in Template::Toolkit — though you're still free to suggest improvements :):

      // BEGIN [% IF list -%] switch(x) { [% FOREACH item IN list -%] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% END -%] [% END -%] } [% END -%] // END

      Run together with the following code:

      #! perl -w use Template; my $tt = Template->new(); foreach my $data ([undef => undef], [ empty => []], ['("foo", "bar", "baz")' => [qw(foo bar baz)]]) { print "// List is $data->[0]:\n"; $tt->process('tt2.tmpl', { list => $data->[1] }); print "\n\n"; }

      Oh, and I feel that I had to tweak it quite a bit just to get the proper amount of whitespace, adding the hyphens just at the right spots (those -%] thingies). Either I'm missing something, or Template::Toolkit could use a touchup.

      I'd also like to indent the loop control directives, without that the added leading whitespace shows up in the output. Can it be done? It seems like I have to choose between stripping all whitespace (including newlines) just in front or right after the directives, or no stripping at all. Can't I strip leading whitespace just up to, but excluding, the newline?

        // BEGIN [% IF list -%] switch(x) { [% FOREACH item IN list -%] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% END -%] [% END -%] } [% END -%] // END
        With loop.first and loop.last (which I sometimes wish Perl had), you can simplify this even further by getting rid of the outer "if":
        [% FOREACH item IN list -%] [% IF loop.first -%] // BEGIN switch(x) { [% END %] case [% item %]: // code for [% item %] [% UNLESS loop.last -%] break; [% ELSE -%] } // END [% END -%]
        The loop won't be run if there are no elements, so you don't need to test if elements exist or not. Then, once in the loop, you put "ahead of first time" and "after last time" things inside the loop.first and loop.last conditionals. Nice pattern. In the abstract:
        [% FOR item IN list %] [% IF loop.first %] ... before first item .. [% END %] ... each item ... [% UNLESS loop.last %] ... between items ... [% ELSE %] ... after last item ... [% END; END %]

        -- Randal L. Schwartz, Perl hacker
        Be sure to read my standard disclaimer if this is a reply.

        Oh, and I feel that I had to tweak it quite a bit just to get the proper amount of whitespace, adding the hyphens just at the right spots (those -%] thingies). Either I'm missing something, or Template::Toolkit could use a touchup.

        You can control this behavior in a more global manner with the chomping options PRE_CHOMP and POST_CHOMP which you can set when you create your Template object. You can find some details on the TT web site in the section on config options.

Re: Templating suggestions for code generation
by qq (Hermit) on Feb 17, 2005 at 16:44 UTC

    Nothing wrong with the other suggestions, but HTML::Template can do something similar:

    //START <tmpl_loop name="cases"> <tmpl_if name="__first__"> switch(x) { </tmpl_if> case <tmpl_var name="case": // <tmpl_var name="case_code"> <tmpl_unless NAME="__last__"> break; <tmpl_else> } </tmpl_unless> //END

    Loops have __first__, __last__, __inner__, __odd__ and __counter__.

Jeeves and StringTemplate
by Anonymous Monk on Feb 17, 2005 at 18:18 UTC
    Take a look at the "Jeeves" code generator in my book, Advanced Perl Programming. The template syntax is similar to yours. (The code is freely available on the oreilly site) It might be worth your while to also study Terence Parr's StringTemplate , esp. the publications section. This template engine enforces strict separation between model and view, so there is no mixture of code and output text. --Sriram.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://431797]
Approved by Tanktalus
Front-paged by Tanktalus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (5)
As of 2024-04-24 10:35 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found