src/parser/Parser.yp - lemplate

  1. #============================================================= -*-Perl-*-
  2. #
  3. # Parser.yp
  4. #
  5. # DESCRIPTION
  6. #   Definition of the parser grammar for the Template Toolkit language.
  7. #
  8. # AUTHOR
  9. #   Ingy döt Net   <ingy@cpan.org>
  10. #
  11. # ORIGINAL AUTHOR
  12. #   Andy Wardley   <abw@kfs.org>
  13. #
  14. # HISTORY
  15. #   Totally re-written for version 2, based on Doug Steinwand's
  16. #   implementation which compiles templates to Perl code.  The generated
  17. #   code is _considerably_ faster, more portable and easier to process.
  18. #
  19. # WARNINGS
  20. #   Expect 1 reduce/reduce conflict.  This can safely be ignored.
  21. #   Now also expect 1 shift/reduce conflict, created by adding a rule
  22. #   to 'args' to allow assignments of the form 'foo.bar = baz'.  It
  23. #   should be possible to fix the problem by rewriting some rules, but
  24. #   I'm loathed to hack it up too much right now.  Maybe later.
  25. #
  26. # COPYRIGHT
  27. #   Copyright (C) 2006,2008 Ingy döt Net.
  28. #   Copyright (C) 1996-2004 Andy Wardley.
  29. #   Copyright (C) 1998-2004 Canon Research Centre Europe Ltd.
  30. #
  31. #   This module is free software; you can redistribute it and/or
  32. #   modify it under the same terms as Perl itself.
  33. #
  34. #------------------------------------------------------------------------
  35. #
  36. # NOTE: this module is constructed from the parser/Grammar.pm.skel
  37. # file by running the parser/yc script.  You only need to do this if
  38. # you have modified the grammar in the parser/Parser.yp file and need
  39. # to-recompile it.  See the README in the 'parser' directory for more
  40. # information (sub-directory of the Template distribution).
  41. #
  42. #------------------------------------------------------------------------
  43. #
  44. # $Id: Parser.yp,v 2.20 2004/01/13 15:32:22 abw Exp $
  45. #
  46. #========================================================================

  47. %right ASSIGN
  48. %right '?' ':'
  49. %left COMMA
  50. %left AND OR
  51. %left NOT
  52. %left CAT
  53. %left DOT
  54. %left CMPOP
  55. %left BINOP
  56. %left '+'
  57. %left '/'
  58. %left DIV
  59. %left MOD
  60. %left TO
  61. %%

  62. #--------------------------------------------------------------------------
  63. # START AND TOP-LEVEL RULES
  64. #--------------------------------------------------------------------------

  65. template:   block          { $factory->template($_[1])           }
  66. ;

  67. block:       chunks          { $factory->block($_[1])              }
  68.    |   /* NULL */          { $factory->block()                   }
  69. ;

  70. chunks:       chunks chunk       { push(@{$_[1]}, $_[2])
  71.                if defined $_[2]; $_[1]           }
  72.     |   chunk          { defined $_[1] ? [ $_[1] ] : [ ]     }
  73. ;

  74. chunk:  TEXT             { $factory->textblock($_[1])          }
  75.     |   statement ';'    { return '' unless $_[1];
  76.                            $_[0]->location() . $_[1];
  77.                          }
  78. ;

  79. statement:  directive
  80.     |   defblock
  81.    |   anonblock
  82.    |   capture
  83.    |   macro
  84.    |   use
  85.    |   raw
  86.    |   view
  87.    |   rawperl
  88.    |   expr          { $factory->get($_[1])                }
  89.    |   META metadata        { $_[0]->add_metadata($_[2]);         }
  90.    |   /* empty statement */
  91. ;

  92. directive:  setlist          { $factory->set($_[1])                }
  93.    |   atomdir
  94.    |   condition
  95.    |   switch
  96.    |   loop
  97.    |   try
  98.         |   javascript
  99.    |   perl
  100. ;


  101. #--------------------------------------------------------------------------
  102. # DIRECTIVE RULES
  103. #--------------------------------------------------------------------------

  104. atomexpr:   expr          { $factory->get($_[1])                }
  105.    |   atomdir
  106. ;

  107. atomdir:    GET expr          { $factory->get($_[2])                }
  108.    |   CALL expr          { $factory->call($_[2])               }
  109.    |   SET setlist           { $factory->set($_[2])                }
  110.    |   DEFAULT setlist         { $factory->default($_[2])            }
  111.    |   INSERT nameargs         { $factory->insert($_[2])             }
  112.    |   INCLUDE nameargs       { $factory->include($_[2])            }
  113.    |   PROCESS nameargs       { $factory->process($_[2])            }
  114.    |   THROW nameargs          { $factory->throw($_[2])              }
  115.    |   RETURN          { $factory->return()                  }
  116.    |   STOP          { $factory->stop()                    }
  117.    |   CLEAR                   { $factory->clear()                   }
  118.    |   LAST                    { $factory->break()                   }
  119.    |   NEXT          { $factory->next()                    }
  120.    |   DEBUG nameargs          { if ($_[2]->[0]->[0] =~ /^'(on|off)'$/) {
  121.                       $_[0]->{ DEBUG_DIRS } = ($1 eq 'on');
  122.                  $factory->debug($_[2]);
  123.                   }
  124.                   else {
  125.                  $_[0]->{ DEBUG_DIRS } ? $factory->debug($_[2]) : '';
  126.                   }
  127.                 }
  128.         |   wrapper
  129.    |   filter
  130. ;

  131. condition:  IF expr ';'
  132.          block else END       { $factory->if(@_[2, 4, 5])           }
  133.    |   atomexpr IF expr       { $factory->if(@_[3, 1])              }
  134.    |   UNLESS expr ';'
  135.          block else END       { $factory->if("tt2_not($_[2])", @_[4, 5])  }
  136.    |   atomexpr UNLESS expr    { $factory->if("tt2_not($_[3])", $_[1])     }
  137. ;

  138. else:       ELSIF expr ';'
  139.          block else       { unshift(@{$_[5]}, [ @_[2, 4] ]);
  140.                   $_[5];                              }
  141.    |   ELSE ';' block       { [ $_[3] ]                           }
  142.    |   /* NULL */              { [ undef ]                           }
  143. ;

  144. switch:       SWITCH expr ';'
  145.          block case END       { $factory->switch(@_[2, 5])          }
  146. ;

  147. case:       CASE term ';' block
  148.          case          { unshift(@{$_[5]}, [ @_[2, 4] ]);
  149.                   $_[5];                              }
  150.    |   CASE DEFAULT ';' block  { [ $_[4] ]                           }
  151.    |   CASE ';' block       { [ $_[3] ]                           }
  152.    |   /* NULL */          { [ undef ]                           }
  153. ;

  154. loop:       FOR loopvar ';'         { $_[0]->{ INFOR }++                  }
  155.       block END       { $_[0]->{ INFOR }--;
  156.                   $factory->foreach(@{$_[2]}, $_[5])  }
  157. #loop:       FOR loopvar ';'
  158. #      block END       { $factory->foreach(@{$_[2]}, $_[4])  }
  159.    |   atomexpr FOR loopvar    { $factory->foreach(@{$_[3]}, $_[1])  }
  160.    |   WHILE expr ';'          { $_[0]->{ INWHILE }++                }
  161.          block END          { $_[0]->{ INWHILE }--;
  162.                                       $factory->while(@_[2, 5])           }
  163.    |   atomexpr WHILE expr       { $factory->while(@_[3, 1])           }
  164. ;

  165. loopvar:    IDENT ASSIGN term args  { [ @_[1, 3, 4] ]                     }
  166.         |   IDENT IN term args      { [ @_[1, 3, 4] ]                     }
  167.    |   term args               { [ 0, @_[1, 2] ]                     }
  168. ;

  169. wrapper:    WRAPPER nameargs ';'
  170.          block END          { $factory->wrapper(@_[2, 4])         }
  171.    |   atomexpr
  172.          WRAPPER nameargs       { $factory->wrapper(@_[3, 1])         }
  173. ;

  174. try:       TRY ';'
  175.          block final END       { $factory->try(@_[3, 4])             }
  176. ;

  177. final:       CATCH filename ';'
  178.          block final       { unshift(@{$_[5]}, [ @_[2,4] ]);
  179.                   $_[5];                              }
  180.    |   CATCH DEFAULT ';'
  181.          block final       { unshift(@{$_[5]}, [ undef, $_[4] ]);
  182.                   $_[5];                              }
  183.    |   CATCH ';'
  184.          block final       { unshift(@{$_[4]}, [ undef, $_[3] ]);
  185.                   $_[4];                              }
  186.    |    FINAL ';' block       { [ $_[3] ]                           }
  187.    |   /* NULL */          { [ 0 ] } # no final
  188. ;

  189. use:       USE lnameargs       { $factory->use($_[2])                }
  190. ;

  191. raw:       RAW lnameargs       { $factory->raw($_[2])                }
  192. ;

  193. view:       VIEW nameargs ';'       { $_[0]->push_defblock();        }
  194.          block END             { $factory->view(@_[2,5],
  195.                        $_[0]->pop_defblock) }
  196. ;

  197. javascript: JAVASCRIPT ';'                { ${$_[0]->{ INJAVASCRIPT }}++;             }
  198.          block END          { ${$_[0]->{ INJAVASCRIPT }}--;
  199.                   $_[0]->{ EVAL_JAVASCRIPT }
  200.                   ? $factory->javascript($_[4])
  201.                   : $factory->no_javascript();              }
  202. ;

  203. filter:       FILTER lnameargs ';'
  204.          block END          { $factory->filter(@_[2,4])           }
  205.    |   atomexpr FILTER
  206.          lnameargs          { $factory->filter(@_[3,1])           }
  207. ;

  208. defblock: defblockname
  209.           blockargs ';'
  210.           template END          { my $name = join('/', @{ $_[0]->{ DEFBLOCKS } });
  211.                   pop(@{ $_[0]->{ DEFBLOCKS } });
  212.                   $_[0]->define_block($name, $_[4]);
  213.                   undef
  214.                 }
  215. ;

  216. defblockname: BLOCK blockname       { push(@{ $_[0]->{ DEFBLOCKS } }, $_[2]);
  217.                   $_[2];
  218.                 }
  219. ;

  220. blockname:  filename
  221.         |   LITERAL          { $_[1] =~ s/^'(.*)'$/$1/; $_[1]      }
  222. ;

  223. blockargs:  metadata
  224.    |   /* NULL */
  225. ;

  226. anonblock:  BLOCK blockargs ';' block END
  227.                 { local $" = ', ';
  228.                   print STDERR "experimental block args: [@{ $_[2] }]\n"
  229.                  if $_[2];
  230.                   $factory->anon_block($_[4])         }
  231. ;

  232. capture:    ident ASSIGN mdir       { $factory->capture(@_[1, 3])         }
  233. ;

  234. macro:      MACRO IDENT '(' margs ')'
  235.       mdir                { $factory->macro(@_[2, 6, 4])        }
  236.    |   MACRO IDENT mdir        { $factory->macro(@_[2, 3])           }
  237. ;

  238. mdir:       directive
  239.    |   BLOCK ';' block END       { $_[3]                               }
  240. ;

  241. margs:       margs IDENT          { push(@{$_[1]}, $_[2]); $_[1]        }
  242.    |   margs COMMA             { $_[1]                               }
  243.    |   IDENT                   { [ $_[1] ]                           }
  244. ;

  245. metadata:   metadata meta       { push(@{$_[1]}, @{$_[2]}); $_[1]     }
  246.    |   metadata COMMA
  247.    |   meta
  248. ;

  249. meta:       IDENT ASSIGN LITERAL       { for ($_[3]) { s/^'//; s/'$//;
  250.                          s/\\'/'/g  };
  251.                 [ @_[1,3] ] }
  252.    |   IDENT ASSIGN '"' TEXT '"'  { [ @_[1,4] ] }
  253.    |   IDENT ASSIGN NUMBER        { [ @_[1,3] ] }
  254. ;


  255. #--------------------------------------------------------------------------
  256. # FUNDAMENTAL ELEMENT RULES
  257. #--------------------------------------------------------------------------

  258. term:       lterm
  259.    |   sterm
  260. ;

  261. lterm:       '[' list  ']'       { "{ $_[2] }"                         }
  262.    |   '[' range ']'           { "{ $_[2] }"                         }
  263.    |   '['       ']'           { "{ }"                               }
  264.    |   '{' hash  '}'       { "{ $_[2]  }"                        }
  265. ;

  266. sterm:       ident          { $factory->ident($_[1])              }
  267.    |   REF ident               { $factory->identref($_[2])           }
  268.    |   '"' quoted '"'       { $factory->quoted($_[2])             }
  269.    |   LITERAL
  270.    |   NUMBER
  271. ;

  272. list:       list term          { "$_[1], $_[2]"                      }
  273.    |   list COMMA
  274.    |   term
  275. ;

  276. range:       sterm TO sterm       { $_[1] . '..' . $_[3]                }
  277. ;


  278. hash:       params
  279.    |   /* NULL */          { "" }
  280. ;

  281. params:       params param       { "$_[1], $_[2]"                      }
  282.    |   params COMMA
  283.    |   param
  284. ;

  285. param:      LITERAL ASSIGN expr     { "[$_[1]] = $_[3]"                    }
  286.         |   item ASSIGN expr        { "[$_[1]] = $_[3]"                    }
  287. ;

  288. ident:      ident DOT node       { push(@{$_[1]}, @{$_[3]}); $_[1]     }
  289.    |   ident DOT NUMBER       { push(@{$_[1]},
  290.                   map {($_, 0)} split(/\./, $_[3]));
  291.                   $_[1];                   }
  292.    |   node
  293. ;

  294. node:       item          { [ $_[1], 0 ]                        }
  295.    |   item '(' args ')'       { [ $_[1], $factory->args($_[3]) ]    }
  296. ;

  297. item:       IDENT          { "'$_[1]'"                           }
  298.    |   '${' sterm '}'       { $_[2]                               }
  299.    |   '$' IDENT          { $_[0]->{ V1DOLLAR }
  300.                    ? "'$_[2]'"
  301.                    : $factory->ident(["'$_[2]'", 0])  }
  302. ;

  303. expr:       expr BINOP expr       { "$_[1] $_[2] $_[3]"                 }
  304.    |   expr '/' expr       { "$_[1] $_[2] $_[3]"                 }
  305.    |   expr '+' expr       { "$_[1] $_[2] $_[3]"                 }
  306.    |   expr DIV expr       { "math_floor($_[1] / $_[3])"         }
  307.    |   expr MOD expr       { "$_[1] % $_[3]"                     }
  308.    |   expr CMPOP expr       { "$_[1] $CMPOP{ $_[2] } $_[3]"       }
  309.    |   expr CAT expr       { "$_[1] .. $_[3]"                    }
  310.    |   expr AND expr       { "tt2_true($_[1]) and tt2_true($_[3])"                   }
  311.    |   expr OR expr       { "tt2_true($_[1]) or tt2_true($_[3])"                    }
  312.    |   NOT expr          { "tt2_not($_[2])"                         }
  313.    |   expr '?' expr ':' expr  { "tt2_true($_[1]) and $_[3] or $_[5]"          }
  314.    |   '(' assign ')'       { $factory->assign(@{$_[2]})          }
  315.    |   '(' expr ')'       { "($_[2])"                           }
  316.    |   term
  317. ;

  318. setlist:    setlist assign       { push(@{$_[1]}, @{$_[2]}); $_[1]     }
  319.    |   setlist COMMA
  320.    |   assign
  321. ;


  322. assign:       ident ASSIGN expr       { [ $_[1], $_[3] ]                    }
  323.    |   LITERAL ASSIGN expr       { [ @_[1,3] ]                         }
  324. ;

  325. # The 'args' production constructs a list of named and positional
  326. # parameters.  Named parameters are stored in a list in element 0
  327. # of the args list.  Remaining elements contain positional parameters

  328. args:       args expr          { push(@{$_[1]}, $_[2]); $_[1]        }
  329.    |   args param              { push(@{$_[1]->[0]}, $_[2]); $_[1]   }
  330.    |   args ident ASSIGN expr  { push(@{$_[1]->[0]}, "'', " .
  331.                   $factory->assign(@_[2,4])); $_[1]  }
  332.    |   args COMMA          { $_[1]                               }
  333.    |   /* init */          { [ [ ] ]                             }
  334. ;


  335. # These are special case parameters used by INCLUDE, PROCESS, etc., which
  336. # interpret barewords as quoted strings rather than variable identifiers;
  337. # a leading '$' is used to explicitly specify a variable.  It permits '/',
  338. # '.' and '::' characters, allowing it to be used to specify filenames, etc.
  339. # without requiring quoting.

  340. lnameargs:  lvalue ASSIGN nameargs  { push(@{$_[3]}, $_[1]); $_[3]        }
  341.    |   nameargs
  342. ;

  343. lvalue:       item
  344.    |   '"' quoted '"'       { $factory->quoted($_[2])             }
  345.    |   LITERAL
  346. ;

  347. nameargs:   '$' ident args       { [ [$factory->ident($_[2])], $_[3] ]   }
  348.    |   names args          { [ @_[1,2] ] }
  349.    |   names '(' args ')'       { [ @_[1,3] ] }
  350. ;

  351. names:       names '+' name       { push(@{$_[1]}, $_[3]); $_[1] }
  352.    |   name          { [ $_[1] ]                    }
  353. ;

  354. name:       '"' quoted '"'       { $factory->quoted($_[2])  }
  355.    |   filename                { "'$_[1]'" }
  356.    |    LITERAL
  357. ;

  358. #nameargs:   literal args       { [ @_[1,2] ] }
  359. #   |   literal '(' args ')'    { [ @_[1,3] ] }
  360. #   |   '$' ident
  361. #;

  362. #namesargs:  names args          { [ @_[1,2] ] }
  363. #;

  364. filename:   filename DOT filepart   { "$_[1].$_[3]" }
  365.    |   filepart
  366. ;

  367. filepart: FILENAME | IDENT | NUMBER
  368. ;


  369. # The 'quoted' production builds a list of 'quotable' items that might
  370. # appear in a quoted string, namely text and identifiers.  The lexer
  371. # adds an explicit ';' after each directive it finds to help the
  372. # parser identify directive/text boundaries; we're not interested in
  373. # them here so we can simply accept and ignore by returning undef

  374. quoted:       quoted quotable       { push(@{$_[1]}, $_[2])
  375.                       if defined $_[2]; $_[1]         }
  376.    |   /* NULL */          { [ ]                                 }
  377. ;

  378. quotable:   ident          { $factory->ident($_[1])              }
  379.    |   TEXT          { $factory->text($_[1])               }
  380.    |   ';'                  { undef                               }
  381. ;


  382. %%