# # Copyright (c) 2015 Will Bond, Mjumbe Wawatu Ukweli, 2012 Canonical Ltd # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Lesser General Public License as published by # the Free Software Foundation, version 3 only. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . # GNU Lesser General Public License version 3 (see the file LICENSE). """The compiler for pybars.""" import os import re import sys from types import ModuleType import linecache # The following code allows importing pybars from the current dir prev_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if sys.path[0] != prev_dir: try: sys.path.remove(prev_dir) except ValueError: pass sys.path.insert(0, prev_dir) import pybars from pybars import _templates from .pymeta.grammar import OMeta __all__ = [ 'Compiler', 'strlist', 'Scope' ] __metaclass__ = type # This allows the code to run on Python 2 and 3 by # creating a consistent reference for the appropriate # string class try: str_class = unicode except NameError: # Python 3 support str_class = str # backward compatibility for basestring for python < 2.3 try: basestring except NameError: basestring = str # Flag for testing debug = False # Note that unless we presume handlebars is only generating valid html, we have # to accept anything - so a broken template won't be all that visible - it will # just render literally (because the anything rule matches it). # this grammar generates a tokenised tree handlebars_grammar = r""" template ::= ( | )*:body => ['template'] + body text ::= | | newline_text ::= (~() ('\r'?'\n'):char) => ('newline', u'' + char) whitespace_text ::= (~() (' '|'\t'))+:text => ('whitespace', u''.join(text)) other_text ::= (~() )+:text => ('literal', u''.join(text)) other ::= :char => ('literal', u'' + char) templatecommand ::= | | | | | start ::= '{' '{' finish ::= '}' '}' comment ::= '!' (~() )* => ('comment', ) space ::= ' '|'\t'|'\r'|'\n' arguments ::= (+ (|||))*:arguments => arguments subexpression ::= '(' :p (+ (|||))*:arguments ')' => ('subexpr', p, arguments) expression_inner ::= :p :arguments => (p, arguments) expression ::= '{' :e '}' => ('expand', ) + e | '&' :e => ('expand', ) + e escapedexpression ::= :e => ('escapedexpand', ) + e block_inner ::= :s :args => (u''.join(s), args) partial_inner ::= :s :args => (s, args) alt_inner ::= ('^' | 'e' 'l' 's' 'e') partial ::= '>' :i => ('partial',) + i path ::= ~('/') +:segments => ('path', segments) kwliteral ::= :s '=' (||):v => ('kwparam', s, v) literal ::= (||||):thing => ('literalparam', thing) string ::= '"' *:ls '"' => u'"' + u''.join(ls) + u'"' | "'" *:ls "'" => u"'" + u''.join(ls) + u"'" integer ::= '-'?:sign +:ds => int((sign if sign else '') + ''.join(ds)) boolean ::= | false ::= 'f' 'a' 'l' 's' 'e' => False true ::= 't' 'r' 'u' 'e' => True null ::= ('n' 'u' 'l' 'l') => None undefined ::= ('u' 'n' 'd' 'e' 'f' 'i' 'n' 'e' 'd') => None notdquote ::= | '\n' => '\\n' | '\r' => '\\r' | '\\' => '\\\\' | (~('"') ) notsquote ::= | '\n' => '\\n' | '\r' => '\\r' | '\\' => '\\\\' | (~("'") ) escapedquote ::= '\\' '"' => '\\"' | "\\" "'" => "\\'" notclosebracket ::= (~(']') ) safesymbol ::= ~ '['? (|'_'):start (|'_')+:symbol ']'? => start + u''.join(symbol) symbol ::= ~ '['? (|'-'|'@')+:symbol ']'? => u''.join(symbol) partialname ::= :s => s | ~ ('['|'"')? (~(||']'|'"' ) )+:symbol (']'|'"')? => ('literalparam', '"' + u''.join(symbol) + '"') pathseg ::= '[' +:symbol ']' => u''.join(symbol) | ('@' '.' '.' '/') => u'@@_parent' | | '/' => u'' | ('.' '.' '/') => u'@_parent' | '.' => u'' pathfinish :expected ::= '/' :found ?(found == expected) symbolfinish :expected ::= '/' :found ?(found == expected) blockrule ::= '#' :i