# Контекстно-свободная Parse::Yapp-грамматика шаблонизатора # # Компилировать так: yapp -s -o VMXTemplate/Parser.pm -t template.skel.pm template.yp # # {{ двойные скобки }} нужно исключительно чтобы маркеры начала и конца подстановки # были уникальны в грамматике. Вместо них обычно используются { одинарные }, а # выбор корректной лексемы - скобки или маркера - делает лексический анализатор. # Но зато вместо { фигурных скобок } можно выбрать себе любые другие маркеры! # # Все выражения представляются массивами из двух-трёх значений: # [ код выражения, флаг экранирования, флаг принудительной подстановки ] # Флаг экранирования == true, если это выражение HTML-безопасно, и 'i', если оно не только # HTML-безопасно, но ещё и численно. При включённом auto_escape небезопасные выражения # прогоняются через экранирование (обычно через 's'). # Флаг принудительной подстановки используется функциями типа INCLUDE, чтобы подставлять результат, # даже когда no_code_subst == true. # # Кстати: # * Олдстайл BEGIN .. END ликвидирован # * Возможно, нужно добавить в каком-то виде foreach ... as key => value # # P.S: Комментарии типа "#{" и "#}" служат, чтобы у тупого Parse::Yapp'а число скобок сходилось %start template %token literal %token name %token '..' %token '||' %token 'OR' %token 'XOR' %token 'AND' %token '&&' %token '&' %token '==' %token '!=' %token '<' %token '>' %token '<=' %token '>=' %token '+' %token '-' %token '*' %token '/' %token '%' %token '(' %token ')' %token '!' %token 'NOT' %token '{' %token '}' %token ',' %token '=>' %token '[' %token ']' %token '' %token '{{' %token '}}' %left '..' %left '||' 'OR' 'XOR' %left '&&' 'AND' %nonassoc '==' '!=' '<' '>' '<=' '>=' %left '+' '-' %left '&' %left '*' '/' '%' # Директивы %% template: chunks { $_[0]->{functions}->{':main'}->{body} = "sub {\nmy \$self = shift;\nmy \$stack = [];\nmy \$t = '';\n".$_[1]."\nreturn \$t;\n}\n"; ''; } ; chunks: { ''; } | chunks error { # Exit error recovery $_[0]->YYErrok; # Skip current token ${$_[0]->{TOKEN}} = undef; $_[1]; } | chunks chunk { $_[1] . '# line '.(1+$_[0]->{lexer}->{lineno}).' "'.$_[0]->{options}->{input_filename}."\"\n". $_[2]; } ; chunk: literal { ($_[1][0] ne "''" && $_[1][0] ne '""' ? '$t .= ' . $_[1][0] . ";\n" : ''); } | '' { $_[2]; } | '{{' exp '}}' { '$t .= ' . ($_[2][1] || !$_[0]->{options}->{auto_escape} ? $_[2][0] : $_[0]->compile_function($_[0]->{options}->{auto_escape}, [ $_[2] ])->[0]) . ";\n"; } ; code_chunk: c_if | c_set | c_fn | c_for | exp { ($_[1][2] || !$_[0]->{options}->{no_code_subst} ? '$t .= ' : '') . ($_[1][1] || !$_[0]->{options}->{auto_escape} ? $_[1][0] : $_[0]->compile_function($_[0]->{options}->{auto_escape}, [ $_[1] ])->[0]) . ";\n"; } ; c_if: 'IF' exp '-->' chunks '' chunks '' chunks '' chunks c_elseifs chunks '' chunks c_elseifs chunks '' chunks '' { #{ "} elsif (" . $_[3][0] . ") {\n"; #} } | c_elseifs chunks '' { #{ $_[1] . $_[2] . "} elsif (" . $_[5][0] . ") {\n"; #} } ; c_set: 'SET' varref '=' exp { $_[2][0] . ' = ' . $_[4][0] . ";\n"; } | 'SET' varref '-->' chunks '' chunks '' chunks '