[Latte] words and variables

bobg@zanshin.com bobg@zanshin.com
Fri, 30 Mar 2001 16:12:08 -0800


Daniel Mahler writes:
> The problem is that I want the name of the refernce, without the "\"
> to appear in the output of the template
> 
> Something like
> {\template \T}
> 
> {\T 1}
> => T1

I don't want to get too much into Blatte on this list, but let's
suspend that rule for the duration of this message.  There are a
couple of ways to do this in Blatte.

The first way is the easier one: instead of getting the name of a
variable, you turn a name into a variable.  So you'd write

  {\template T}

to get

  {\T 1}
  => T1

You do this by defining \template in Perl, like so:

  use Blatte qw(unwrapws);

  $template = sub {
    my $name = unwrapws($_[1]);
    eval sprintf(q[$%s = sub {
      my $suffix = unwrapws($_[1]);
      '%s' . $suffix;
    }], $name, $name);
  };

and placing the definition in a file that you can "use" (in Perl) or
{\use ...} (in Blatte), or in a context where it's visible when you
eval the Blatte program that uses {\template ...}.

The harder, second way -- getting the name of the variable -- requires
making {\template ...} into a syntactic form, which means extending
the Blatte parser.  (You have to get at the variable's name in the
parsing step, because by the time the parser's done with a variable
reference, the name is effectively impossible to get.)

You'd do this:

  use Blatte::Parser;

  my $parser = new Blatte::Parser();
  $parser->add_special_form(\&template_expr);

Where template_expr is the following parsing function:

  sub template_expr {
    my($self, $input_arg) = @_;

    my $input = $input_arg;
    if (ref($input)) {
      $input = $$input;
    }

    &consume_whitespace(\$input);
    return undef unless ($input =~ /^\\template/);
    $input = substr($input, 9);

    &consume_whitespace(\$input);
    return undef unless ($input =~ /^\\($Blatte::Parser::identifier_regex)/go);
    my $name = $1;
    $input = substr($input, pos($input));

    if (ref($input_arg)) {
      $$input_arg = $input;
    }

    return new TemplateSyntax($name);
  }

and TemplateSyntax, representing the parsed {\template ...}
expression, is the following module:

  package TemplateSyntax;

  sub new {
    my($type, $name) = @_;
    bless \$name, $type;
  }

  sub transform {
    my $self = shift;
    my $name = $$self;
    sprintf(q[$%s = sub { my $suffix = unwrapws($_[1]); '%s' . $suffix }],
            $name, $name);
  }

(The transform() function of a syntax object turns parsed Blatte into
the equivalent executable Perl.)

> One application of this is to automatically generate tag functions
> for XML DTDs by instantiating a definition template.
> 
> {\element \img \src}
> 
> should generate the definition
> 
> {\def {\img \=src}
>   {... img ... src }}

Again, if you're willing to descend into the target language (Perl),
you can do this pretty simply.  Blatte::HTML defines an object
representing an HTML element, and then instantiates Blatte functions,
each of which produces one of these element objects, like this:

    $a          = sub { new Blatte::HTML::Element('a',       @_) };
    $abbr       = sub { new Blatte::HTML::Element('abbr',    @_) };
    $acronym    = sub { new Blatte::HTML::Element('acronym', @_) };
    $address    = sub { new Blatte::HTML::Element('address', @_) };

    ...and so on...

Does this help?

Cheers,
- Bob