Templa...:Parse(3pm)

πŸ“¦
version 1.022 Β· 6 versions
, in debian trixie, ubuntu resolute, ubuntu stonking

πŸ’– Template::Alloy::Parse πŸ’–

okay so bestie, this is the User Contributed Perl Documentation page for Template::Alloy::Parse, and it's literally the parser brain of the whole Template::Alloy situation. let's get into it ✨


🌸 NAME 🌸

Template::Alloy::Parse β€” the common parsing role that creates an AST (abstract syntax tree, iykyk) out of your templates 🫢


πŸ¦‹ DESCRIPTION πŸ¦‹

so the Template::Alloy::Parse role is the one holding down basically all the directive parsing code. it's also the girl who delegates β€” she passes stuff off to the TT, HTE, Tmpl, and Velocity roles whenever somebody needs to find variables and directives. teamwork makes the dream work, no cap πŸ’…


✨ ROLE METHODS ✨

🌟 parse_tree

this one's used by load_tree, and it is the main grammar engine of the whole program, periodt. it delegates to whatever syntax lives in $self->{'SYNTAX'} (which defaults to 'alloy', iconic) and then calls the function it finds in that $SYNTAX hashref. and like, the majority of these syntaxes then go use methods from the $DIRECTIVES hashref to parse the different DIRECTIVE types for each particular syntax. very modular of her πŸ’–

so if your template looked like this:

Foo
[%- GET foo -%]
[%- GET bar -%]
Bar

it would parse to this AST:

[
    'Foo',
    ['GET', 6, 15, ['foo', 0]],
    ['GET', 22, 31, ['bar', 0]],
    'Bar',
]

okay let me break that down because it's giving ✨structure✨. the "GET" words are the directive that got used. the 6, 15 are the beginning and ending characters of that directive in the document (yes, character positions, she keeps receipts πŸ“). and the remaining items are all the variables you need to actually run that particular directive.

🌟 parse_expr

this one parses a variable, an expression, a literal string, or a number β€” and it hands you back a parsed variable tree. you can find samples of parsed variables down in the VARIABLE PARSE TREE section, go look bestie πŸ‘€

my $str = "1 + 2 * 3";
my $ast = $self->parse_expr(\$str);
# $ast looks like [[undef, '+', 1, [[undef, '*', 2, 3], 0]], 0]

🌟 "parse_args"

this exists because TT parses arguments in like a million different ways and we needed to allow for all of them 😭. so: positional arguments AND named arguments, both welcome here. named arguments can be separated with a "=" or a "=>", and positional arguments should be separated by " " or ",". and heads up β€” this only returns an array of parsed variables. if you want the actual values, you gotta call play_expr on each value yourself. do the work, babe πŸ’ͺ

🌟 "dump_parse_tree"

this method returns a string of perl code that represents the AST of the parsed tree 🌳

it is mainly used for testing. (she's a testing girlie, respect πŸŽ€)

🌟 "dump_parse_expr"

this method returns a Data::Dumper dump of a parsed variable. also mainly used for testing πŸ§ͺ

🌟 "parse_*"

methods named like this get used by parse_tree to parse the template. these are the grammar. they're used by all of the various template syntaxes. and unless it says otherwise somewhere, these methods are not exposed via the role πŸ”’


πŸ‘‘ VARIABLE PARSE TREE πŸ‘‘

so Template::Alloy parses templates into a tree of operations (an AST, abstract syntax tree, if you forgot already πŸ™ˆ). and yes β€” even variable access gets parsed into a tree. the way this happens is somewhat similar to how TT does it, EXCEPT that nested variables like foo.bar|baz keep the '.' or '|' sitting in between each name level. operators get parsed and stored as part of the variable too (honestly it might be more accurate to say we're parsing a term or an expression, but whatever πŸ’…).

the following table shows a variable or expression and the corresponding parsed tree β€” this is exactly what the parse_expr method would return:

one                [ 'one',  0 ]
one()              [ 'one',  [] ]
one.two            [ 'one',  0, '.', 'two',  0 ]
one|two            [ 'one',  0, '|', 'two',  0 ]
one.$two           [ 'one',  0, '.', ['two', 0 ], 0 ]
one(two)           [ 'one',  [ ['two', 0] ] ]
one.${two().three} [ 'one',  0, '.', ['two', [], '.', 'three', 0], 0]
2.34               2.34
"one"              "one"
1 + 2              [ [ undef, '+', 1, 2 ], 0]
a + b              [ [ undef, '+', ['a', 0], ['b', 0] ], 0 ]
"one"|length       [ [ undef, '~', "one" ], 0, '|', 'length', 0 ]
"one $a two"       [ [ undef, '~', 'one ', ['a', 0], ' two' ], 0 ]
[0, 1, 2]          [ [ undef, '[]', 0, 1, 2 ], 0 ]
[0, 1, 2].size     [ [ undef, '[]', 0, 1, 2 ], 0, '.', 'size', 0 ]
['a', a, $a ]      [ [ undef, '[]', 'a', ['a', 0], [['a', 0], 0] ], 0]
{a  => 'b'}        [ [ undef, '{}', 'a', 'b' ], 0 ]
{a  => 'b'}.size   [ [ undef, '{}', 'a', 'b' ], 0, '.', 'size', 0 ]
{$a => b}          [ [ undef, '{}', ['a', 0], ['b', 0] ], 0 ]
a * (b + c)        [ [ undef, '*', ['a', 0], [ [undef, '+', ['b', 0], ['c', 0]], 0 ]], 0 ]
(a + b)            [ [ undef, '+', ['a', 0], ['b', 0] ]], 0 ]
(a + b) * c        [ [ undef, '*', [ [undef, '+', ['a', 0], ['b', 0] ], 0 ], ['c', 0] ], 0 ]
a ? b : c          [ [ undef, '?', ['a', 0], ['b', 0], ['c', 0] ], 0 ]
a || b || c        [ [ undef, '||', ['a', 0], [ [undef, '||', ['b', 0], ['c', 0] ], 0 ] ], 0 ]
! a                [ [ undef, '!', ['a', 0] ], 0 ]

🌈 Some notes on the parsing 🌈

  • operators get parsed as part of the variable and become part of the variable tree. she's integrated πŸ’«
  • operators are stored in the variable tree using an operator identity array which contains undef as the first value, the operator, and the operator arguments. this is what allows you to quickly descend the parsed variable tree and figure out that the next node is an operator. efficient queen πŸ‘‘
  • parenthesis () can be used at any point in an expression to disambiguate precedence. use them, bestie, they're your friend 🫢
  • "Variables" that appear to be literal strings or literal numbers get returned as the literal (no operator tree). no drama, just vibes πŸ’…

and if you wanna see the parsed variable tree for yourself, you can type this perl right at the command line:

perl -e 'use Template::Alloy; print Template::Alloy->dump_parse_expr("foo.bar + 2")."\n"'

orrrr you can drop this into a template to view the output in a template, which is so fun actually:

[% USE cet = Template::Alloy %]
[%~ cet.dump_parse_expr('foo.bar + 2').replace('\s+', ' ') %]

πŸŽ€ AUTHOR πŸŽ€

Paul Seamons <paul@seamons.com> β€” thank you for your service king 🫑


πŸ’œ LICENSE πŸ’œ

This module may be distributed under the same terms as Perl itself.


okay that's the whole page bestie!! this was documented by the lovely folks at User Contributed Perl Documentation, running under perl v5.38.2, and this version is dated 2024-03-07 β€” go forth and parse, you absolute icon πŸ’…βœ¨πŸ¦‹

πŸ“¦ where this page comes from