xref: /aosp_15_r20/external/libchrome/third_party/jinja2/lexer.py (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
1*635a8641SAndroid Build Coastguard Worker# -*- coding: utf-8 -*-
2*635a8641SAndroid Build Coastguard Worker"""
3*635a8641SAndroid Build Coastguard Worker    jinja2.lexer
4*635a8641SAndroid Build Coastguard Worker    ~~~~~~~~~~~~
5*635a8641SAndroid Build Coastguard Worker
6*635a8641SAndroid Build Coastguard Worker    This module implements a Jinja / Python combination lexer. The
7*635a8641SAndroid Build Coastguard Worker    `Lexer` class provided by this module is used to do some preprocessing
8*635a8641SAndroid Build Coastguard Worker    for Jinja.
9*635a8641SAndroid Build Coastguard Worker
10*635a8641SAndroid Build Coastguard Worker    On the one hand it filters out invalid operators like the bitshift
11*635a8641SAndroid Build Coastguard Worker    operators we don't allow in templates. On the other hand it separates
12*635a8641SAndroid Build Coastguard Worker    template code and python code in expressions.
13*635a8641SAndroid Build Coastguard Worker
14*635a8641SAndroid Build Coastguard Worker    :copyright: (c) 2017 by the Jinja Team.
15*635a8641SAndroid Build Coastguard Worker    :license: BSD, see LICENSE for more details.
16*635a8641SAndroid Build Coastguard Worker"""
17*635a8641SAndroid Build Coastguard Workerimport re
18*635a8641SAndroid Build Coastguard Workerfrom collections import deque
19*635a8641SAndroid Build Coastguard Workerfrom operator import itemgetter
20*635a8641SAndroid Build Coastguard Worker
21*635a8641SAndroid Build Coastguard Workerfrom jinja2._compat import implements_iterator, intern, iteritems, text_type
22*635a8641SAndroid Build Coastguard Workerfrom jinja2.exceptions import TemplateSyntaxError
23*635a8641SAndroid Build Coastguard Workerfrom jinja2.utils import LRUCache
24*635a8641SAndroid Build Coastguard Worker
25*635a8641SAndroid Build Coastguard Worker# cache for the lexers. Exists in order to be able to have multiple
26*635a8641SAndroid Build Coastguard Worker# environments with the same lexer
27*635a8641SAndroid Build Coastguard Worker_lexer_cache = LRUCache(50)
28*635a8641SAndroid Build Coastguard Worker
29*635a8641SAndroid Build Coastguard Worker# static regular expressions
30*635a8641SAndroid Build Coastguard Workerwhitespace_re = re.compile(r'\s+', re.U)
31*635a8641SAndroid Build Coastguard Workerstring_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
32*635a8641SAndroid Build Coastguard Worker                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
33*635a8641SAndroid Build Coastguard Workerinteger_re = re.compile(r'\d+')
34*635a8641SAndroid Build Coastguard Worker
35*635a8641SAndroid Build Coastguard Workertry:
36*635a8641SAndroid Build Coastguard Worker    # check if this Python supports Unicode identifiers
37*635a8641SAndroid Build Coastguard Worker    compile('föö', '<unknown>', 'eval')
38*635a8641SAndroid Build Coastguard Workerexcept SyntaxError:
39*635a8641SAndroid Build Coastguard Worker    # no Unicode support, use ASCII identifiers
40*635a8641SAndroid Build Coastguard Worker    name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
41*635a8641SAndroid Build Coastguard Worker    check_ident = False
42*635a8641SAndroid Build Coastguard Workerelse:
43*635a8641SAndroid Build Coastguard Worker    # Unicode support, build a pattern to match valid characters, and set flag
44*635a8641SAndroid Build Coastguard Worker    # to use str.isidentifier to validate during lexing
45*635a8641SAndroid Build Coastguard Worker    from jinja2 import _identifier
46*635a8641SAndroid Build Coastguard Worker    name_re = re.compile(r'[\w{0}]+'.format(_identifier.pattern))
47*635a8641SAndroid Build Coastguard Worker    check_ident = True
48*635a8641SAndroid Build Coastguard Worker    # remove the pattern from memory after building the regex
49*635a8641SAndroid Build Coastguard Worker    import sys
50*635a8641SAndroid Build Coastguard Worker    del sys.modules['jinja2._identifier']
51*635a8641SAndroid Build Coastguard Worker    import jinja2
52*635a8641SAndroid Build Coastguard Worker    del jinja2._identifier
53*635a8641SAndroid Build Coastguard Worker    del _identifier
54*635a8641SAndroid Build Coastguard Worker
55*635a8641SAndroid Build Coastguard Workerfloat_re = re.compile(r'(?<!\.)\d+\.\d+')
56*635a8641SAndroid Build Coastguard Workernewline_re = re.compile(r'(\r\n|\r|\n)')
57*635a8641SAndroid Build Coastguard Worker
58*635a8641SAndroid Build Coastguard Worker# internal the tokens and keep references to them
59*635a8641SAndroid Build Coastguard WorkerTOKEN_ADD = intern('add')
60*635a8641SAndroid Build Coastguard WorkerTOKEN_ASSIGN = intern('assign')
61*635a8641SAndroid Build Coastguard WorkerTOKEN_COLON = intern('colon')
62*635a8641SAndroid Build Coastguard WorkerTOKEN_COMMA = intern('comma')
63*635a8641SAndroid Build Coastguard WorkerTOKEN_DIV = intern('div')
64*635a8641SAndroid Build Coastguard WorkerTOKEN_DOT = intern('dot')
65*635a8641SAndroid Build Coastguard WorkerTOKEN_EQ = intern('eq')
66*635a8641SAndroid Build Coastguard WorkerTOKEN_FLOORDIV = intern('floordiv')
67*635a8641SAndroid Build Coastguard WorkerTOKEN_GT = intern('gt')
68*635a8641SAndroid Build Coastguard WorkerTOKEN_GTEQ = intern('gteq')
69*635a8641SAndroid Build Coastguard WorkerTOKEN_LBRACE = intern('lbrace')
70*635a8641SAndroid Build Coastguard WorkerTOKEN_LBRACKET = intern('lbracket')
71*635a8641SAndroid Build Coastguard WorkerTOKEN_LPAREN = intern('lparen')
72*635a8641SAndroid Build Coastguard WorkerTOKEN_LT = intern('lt')
73*635a8641SAndroid Build Coastguard WorkerTOKEN_LTEQ = intern('lteq')
74*635a8641SAndroid Build Coastguard WorkerTOKEN_MOD = intern('mod')
75*635a8641SAndroid Build Coastguard WorkerTOKEN_MUL = intern('mul')
76*635a8641SAndroid Build Coastguard WorkerTOKEN_NE = intern('ne')
77*635a8641SAndroid Build Coastguard WorkerTOKEN_PIPE = intern('pipe')
78*635a8641SAndroid Build Coastguard WorkerTOKEN_POW = intern('pow')
79*635a8641SAndroid Build Coastguard WorkerTOKEN_RBRACE = intern('rbrace')
80*635a8641SAndroid Build Coastguard WorkerTOKEN_RBRACKET = intern('rbracket')
81*635a8641SAndroid Build Coastguard WorkerTOKEN_RPAREN = intern('rparen')
82*635a8641SAndroid Build Coastguard WorkerTOKEN_SEMICOLON = intern('semicolon')
83*635a8641SAndroid Build Coastguard WorkerTOKEN_SUB = intern('sub')
84*635a8641SAndroid Build Coastguard WorkerTOKEN_TILDE = intern('tilde')
85*635a8641SAndroid Build Coastguard WorkerTOKEN_WHITESPACE = intern('whitespace')
86*635a8641SAndroid Build Coastguard WorkerTOKEN_FLOAT = intern('float')
87*635a8641SAndroid Build Coastguard WorkerTOKEN_INTEGER = intern('integer')
88*635a8641SAndroid Build Coastguard WorkerTOKEN_NAME = intern('name')
89*635a8641SAndroid Build Coastguard WorkerTOKEN_STRING = intern('string')
90*635a8641SAndroid Build Coastguard WorkerTOKEN_OPERATOR = intern('operator')
91*635a8641SAndroid Build Coastguard WorkerTOKEN_BLOCK_BEGIN = intern('block_begin')
92*635a8641SAndroid Build Coastguard WorkerTOKEN_BLOCK_END = intern('block_end')
93*635a8641SAndroid Build Coastguard WorkerTOKEN_VARIABLE_BEGIN = intern('variable_begin')
94*635a8641SAndroid Build Coastguard WorkerTOKEN_VARIABLE_END = intern('variable_end')
95*635a8641SAndroid Build Coastguard WorkerTOKEN_RAW_BEGIN = intern('raw_begin')
96*635a8641SAndroid Build Coastguard WorkerTOKEN_RAW_END = intern('raw_end')
97*635a8641SAndroid Build Coastguard WorkerTOKEN_COMMENT_BEGIN = intern('comment_begin')
98*635a8641SAndroid Build Coastguard WorkerTOKEN_COMMENT_END = intern('comment_end')
99*635a8641SAndroid Build Coastguard WorkerTOKEN_COMMENT = intern('comment')
100*635a8641SAndroid Build Coastguard WorkerTOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
101*635a8641SAndroid Build Coastguard WorkerTOKEN_LINESTATEMENT_END = intern('linestatement_end')
102*635a8641SAndroid Build Coastguard WorkerTOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
103*635a8641SAndroid Build Coastguard WorkerTOKEN_LINECOMMENT_END = intern('linecomment_end')
104*635a8641SAndroid Build Coastguard WorkerTOKEN_LINECOMMENT = intern('linecomment')
105*635a8641SAndroid Build Coastguard WorkerTOKEN_DATA = intern('data')
106*635a8641SAndroid Build Coastguard WorkerTOKEN_INITIAL = intern('initial')
107*635a8641SAndroid Build Coastguard WorkerTOKEN_EOF = intern('eof')
108*635a8641SAndroid Build Coastguard Worker
109*635a8641SAndroid Build Coastguard Worker# bind operators to token types
110*635a8641SAndroid Build Coastguard Workeroperators = {
111*635a8641SAndroid Build Coastguard Worker    '+':            TOKEN_ADD,
112*635a8641SAndroid Build Coastguard Worker    '-':            TOKEN_SUB,
113*635a8641SAndroid Build Coastguard Worker    '/':            TOKEN_DIV,
114*635a8641SAndroid Build Coastguard Worker    '//':           TOKEN_FLOORDIV,
115*635a8641SAndroid Build Coastguard Worker    '*':            TOKEN_MUL,
116*635a8641SAndroid Build Coastguard Worker    '%':            TOKEN_MOD,
117*635a8641SAndroid Build Coastguard Worker    '**':           TOKEN_POW,
118*635a8641SAndroid Build Coastguard Worker    '~':            TOKEN_TILDE,
119*635a8641SAndroid Build Coastguard Worker    '[':            TOKEN_LBRACKET,
120*635a8641SAndroid Build Coastguard Worker    ']':            TOKEN_RBRACKET,
121*635a8641SAndroid Build Coastguard Worker    '(':            TOKEN_LPAREN,
122*635a8641SAndroid Build Coastguard Worker    ')':            TOKEN_RPAREN,
123*635a8641SAndroid Build Coastguard Worker    '{':            TOKEN_LBRACE,
124*635a8641SAndroid Build Coastguard Worker    '}':            TOKEN_RBRACE,
125*635a8641SAndroid Build Coastguard Worker    '==':           TOKEN_EQ,
126*635a8641SAndroid Build Coastguard Worker    '!=':           TOKEN_NE,
127*635a8641SAndroid Build Coastguard Worker    '>':            TOKEN_GT,
128*635a8641SAndroid Build Coastguard Worker    '>=':           TOKEN_GTEQ,
129*635a8641SAndroid Build Coastguard Worker    '<':            TOKEN_LT,
130*635a8641SAndroid Build Coastguard Worker    '<=':           TOKEN_LTEQ,
131*635a8641SAndroid Build Coastguard Worker    '=':            TOKEN_ASSIGN,
132*635a8641SAndroid Build Coastguard Worker    '.':            TOKEN_DOT,
133*635a8641SAndroid Build Coastguard Worker    ':':            TOKEN_COLON,
134*635a8641SAndroid Build Coastguard Worker    '|':            TOKEN_PIPE,
135*635a8641SAndroid Build Coastguard Worker    ',':            TOKEN_COMMA,
136*635a8641SAndroid Build Coastguard Worker    ';':            TOKEN_SEMICOLON
137*635a8641SAndroid Build Coastguard Worker}
138*635a8641SAndroid Build Coastguard Worker
139*635a8641SAndroid Build Coastguard Workerreverse_operators = dict([(v, k) for k, v in iteritems(operators)])
140*635a8641SAndroid Build Coastguard Workerassert len(operators) == len(reverse_operators), 'operators dropped'
141*635a8641SAndroid Build Coastguard Workeroperator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
142*635a8641SAndroid Build Coastguard Worker                         sorted(operators, key=lambda x: -len(x))))
143*635a8641SAndroid Build Coastguard Worker
144*635a8641SAndroid Build Coastguard Workerignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
145*635a8641SAndroid Build Coastguard Worker                            TOKEN_COMMENT_END, TOKEN_WHITESPACE,
146*635a8641SAndroid Build Coastguard Worker                            TOKEN_LINECOMMENT_BEGIN, TOKEN_LINECOMMENT_END,
147*635a8641SAndroid Build Coastguard Worker                            TOKEN_LINECOMMENT])
148*635a8641SAndroid Build Coastguard Workerignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
149*635a8641SAndroid Build Coastguard Worker                             TOKEN_COMMENT, TOKEN_LINECOMMENT])
150*635a8641SAndroid Build Coastguard Worker
151*635a8641SAndroid Build Coastguard Worker
152*635a8641SAndroid Build Coastguard Workerdef _describe_token_type(token_type):
153*635a8641SAndroid Build Coastguard Worker    if token_type in reverse_operators:
154*635a8641SAndroid Build Coastguard Worker        return reverse_operators[token_type]
155*635a8641SAndroid Build Coastguard Worker    return {
156*635a8641SAndroid Build Coastguard Worker        TOKEN_COMMENT_BEGIN:        'begin of comment',
157*635a8641SAndroid Build Coastguard Worker        TOKEN_COMMENT_END:          'end of comment',
158*635a8641SAndroid Build Coastguard Worker        TOKEN_COMMENT:              'comment',
159*635a8641SAndroid Build Coastguard Worker        TOKEN_LINECOMMENT:          'comment',
160*635a8641SAndroid Build Coastguard Worker        TOKEN_BLOCK_BEGIN:          'begin of statement block',
161*635a8641SAndroid Build Coastguard Worker        TOKEN_BLOCK_END:            'end of statement block',
162*635a8641SAndroid Build Coastguard Worker        TOKEN_VARIABLE_BEGIN:       'begin of print statement',
163*635a8641SAndroid Build Coastguard Worker        TOKEN_VARIABLE_END:         'end of print statement',
164*635a8641SAndroid Build Coastguard Worker        TOKEN_LINESTATEMENT_BEGIN:  'begin of line statement',
165*635a8641SAndroid Build Coastguard Worker        TOKEN_LINESTATEMENT_END:    'end of line statement',
166*635a8641SAndroid Build Coastguard Worker        TOKEN_DATA:                 'template data / text',
167*635a8641SAndroid Build Coastguard Worker        TOKEN_EOF:                  'end of template'
168*635a8641SAndroid Build Coastguard Worker    }.get(token_type, token_type)
169*635a8641SAndroid Build Coastguard Worker
170*635a8641SAndroid Build Coastguard Worker
171*635a8641SAndroid Build Coastguard Workerdef describe_token(token):
172*635a8641SAndroid Build Coastguard Worker    """Returns a description of the token."""
173*635a8641SAndroid Build Coastguard Worker    if token.type == 'name':
174*635a8641SAndroid Build Coastguard Worker        return token.value
175*635a8641SAndroid Build Coastguard Worker    return _describe_token_type(token.type)
176*635a8641SAndroid Build Coastguard Worker
177*635a8641SAndroid Build Coastguard Worker
178*635a8641SAndroid Build Coastguard Workerdef describe_token_expr(expr):
179*635a8641SAndroid Build Coastguard Worker    """Like `describe_token` but for token expressions."""
180*635a8641SAndroid Build Coastguard Worker    if ':' in expr:
181*635a8641SAndroid Build Coastguard Worker        type, value = expr.split(':', 1)
182*635a8641SAndroid Build Coastguard Worker        if type == 'name':
183*635a8641SAndroid Build Coastguard Worker            return value
184*635a8641SAndroid Build Coastguard Worker    else:
185*635a8641SAndroid Build Coastguard Worker        type = expr
186*635a8641SAndroid Build Coastguard Worker    return _describe_token_type(type)
187*635a8641SAndroid Build Coastguard Worker
188*635a8641SAndroid Build Coastguard Worker
189*635a8641SAndroid Build Coastguard Workerdef count_newlines(value):
190*635a8641SAndroid Build Coastguard Worker    """Count the number of newline characters in the string.  This is
191*635a8641SAndroid Build Coastguard Worker    useful for extensions that filter a stream.
192*635a8641SAndroid Build Coastguard Worker    """
193*635a8641SAndroid Build Coastguard Worker    return len(newline_re.findall(value))
194*635a8641SAndroid Build Coastguard Worker
195*635a8641SAndroid Build Coastguard Worker
196*635a8641SAndroid Build Coastguard Workerdef compile_rules(environment):
197*635a8641SAndroid Build Coastguard Worker    """Compiles all the rules from the environment into a list of rules."""
198*635a8641SAndroid Build Coastguard Worker    e = re.escape
199*635a8641SAndroid Build Coastguard Worker    rules = [
200*635a8641SAndroid Build Coastguard Worker        (len(environment.comment_start_string), 'comment',
201*635a8641SAndroid Build Coastguard Worker         e(environment.comment_start_string)),
202*635a8641SAndroid Build Coastguard Worker        (len(environment.block_start_string), 'block',
203*635a8641SAndroid Build Coastguard Worker         e(environment.block_start_string)),
204*635a8641SAndroid Build Coastguard Worker        (len(environment.variable_start_string), 'variable',
205*635a8641SAndroid Build Coastguard Worker         e(environment.variable_start_string))
206*635a8641SAndroid Build Coastguard Worker    ]
207*635a8641SAndroid Build Coastguard Worker
208*635a8641SAndroid Build Coastguard Worker    if environment.line_statement_prefix is not None:
209*635a8641SAndroid Build Coastguard Worker        rules.append((len(environment.line_statement_prefix), 'linestatement',
210*635a8641SAndroid Build Coastguard Worker                      r'^[ \t\v]*' + e(environment.line_statement_prefix)))
211*635a8641SAndroid Build Coastguard Worker    if environment.line_comment_prefix is not None:
212*635a8641SAndroid Build Coastguard Worker        rules.append((len(environment.line_comment_prefix), 'linecomment',
213*635a8641SAndroid Build Coastguard Worker                      r'(?:^|(?<=\S))[^\S\r\n]*' +
214*635a8641SAndroid Build Coastguard Worker                      e(environment.line_comment_prefix)))
215*635a8641SAndroid Build Coastguard Worker
216*635a8641SAndroid Build Coastguard Worker    return [x[1:] for x in sorted(rules, reverse=True)]
217*635a8641SAndroid Build Coastguard Worker
218*635a8641SAndroid Build Coastguard Worker
219*635a8641SAndroid Build Coastguard Workerclass Failure(object):
220*635a8641SAndroid Build Coastguard Worker    """Class that raises a `TemplateSyntaxError` if called.
221*635a8641SAndroid Build Coastguard Worker    Used by the `Lexer` to specify known errors.
222*635a8641SAndroid Build Coastguard Worker    """
223*635a8641SAndroid Build Coastguard Worker
224*635a8641SAndroid Build Coastguard Worker    def __init__(self, message, cls=TemplateSyntaxError):
225*635a8641SAndroid Build Coastguard Worker        self.message = message
226*635a8641SAndroid Build Coastguard Worker        self.error_class = cls
227*635a8641SAndroid Build Coastguard Worker
228*635a8641SAndroid Build Coastguard Worker    def __call__(self, lineno, filename):
229*635a8641SAndroid Build Coastguard Worker        raise self.error_class(self.message, lineno, filename)
230*635a8641SAndroid Build Coastguard Worker
231*635a8641SAndroid Build Coastguard Worker
232*635a8641SAndroid Build Coastguard Workerclass Token(tuple):
233*635a8641SAndroid Build Coastguard Worker    """Token class."""
234*635a8641SAndroid Build Coastguard Worker    __slots__ = ()
235*635a8641SAndroid Build Coastguard Worker    lineno, type, value = (property(itemgetter(x)) for x in range(3))
236*635a8641SAndroid Build Coastguard Worker
237*635a8641SAndroid Build Coastguard Worker    def __new__(cls, lineno, type, value):
238*635a8641SAndroid Build Coastguard Worker        return tuple.__new__(cls, (lineno, intern(str(type)), value))
239*635a8641SAndroid Build Coastguard Worker
240*635a8641SAndroid Build Coastguard Worker    def __str__(self):
241*635a8641SAndroid Build Coastguard Worker        if self.type in reverse_operators:
242*635a8641SAndroid Build Coastguard Worker            return reverse_operators[self.type]
243*635a8641SAndroid Build Coastguard Worker        elif self.type == 'name':
244*635a8641SAndroid Build Coastguard Worker            return self.value
245*635a8641SAndroid Build Coastguard Worker        return self.type
246*635a8641SAndroid Build Coastguard Worker
247*635a8641SAndroid Build Coastguard Worker    def test(self, expr):
248*635a8641SAndroid Build Coastguard Worker        """Test a token against a token expression.  This can either be a
249*635a8641SAndroid Build Coastguard Worker        token type or ``'token_type:token_value'``.  This can only test
250*635a8641SAndroid Build Coastguard Worker        against string values and types.
251*635a8641SAndroid Build Coastguard Worker        """
252*635a8641SAndroid Build Coastguard Worker        # here we do a regular string equality check as test_any is usually
253*635a8641SAndroid Build Coastguard Worker        # passed an iterable of not interned strings.
254*635a8641SAndroid Build Coastguard Worker        if self.type == expr:
255*635a8641SAndroid Build Coastguard Worker            return True
256*635a8641SAndroid Build Coastguard Worker        elif ':' in expr:
257*635a8641SAndroid Build Coastguard Worker            return expr.split(':', 1) == [self.type, self.value]
258*635a8641SAndroid Build Coastguard Worker        return False
259*635a8641SAndroid Build Coastguard Worker
260*635a8641SAndroid Build Coastguard Worker    def test_any(self, *iterable):
261*635a8641SAndroid Build Coastguard Worker        """Test against multiple token expressions."""
262*635a8641SAndroid Build Coastguard Worker        for expr in iterable:
263*635a8641SAndroid Build Coastguard Worker            if self.test(expr):
264*635a8641SAndroid Build Coastguard Worker                return True
265*635a8641SAndroid Build Coastguard Worker        return False
266*635a8641SAndroid Build Coastguard Worker
267*635a8641SAndroid Build Coastguard Worker    def __repr__(self):
268*635a8641SAndroid Build Coastguard Worker        return 'Token(%r, %r, %r)' % (
269*635a8641SAndroid Build Coastguard Worker            self.lineno,
270*635a8641SAndroid Build Coastguard Worker            self.type,
271*635a8641SAndroid Build Coastguard Worker            self.value
272*635a8641SAndroid Build Coastguard Worker        )
273*635a8641SAndroid Build Coastguard Worker
274*635a8641SAndroid Build Coastguard Worker
275*635a8641SAndroid Build Coastguard Worker@implements_iterator
276*635a8641SAndroid Build Coastguard Workerclass TokenStreamIterator(object):
277*635a8641SAndroid Build Coastguard Worker    """The iterator for tokenstreams.  Iterate over the stream
278*635a8641SAndroid Build Coastguard Worker    until the eof token is reached.
279*635a8641SAndroid Build Coastguard Worker    """
280*635a8641SAndroid Build Coastguard Worker
281*635a8641SAndroid Build Coastguard Worker    def __init__(self, stream):
282*635a8641SAndroid Build Coastguard Worker        self.stream = stream
283*635a8641SAndroid Build Coastguard Worker
284*635a8641SAndroid Build Coastguard Worker    def __iter__(self):
285*635a8641SAndroid Build Coastguard Worker        return self
286*635a8641SAndroid Build Coastguard Worker
287*635a8641SAndroid Build Coastguard Worker    def __next__(self):
288*635a8641SAndroid Build Coastguard Worker        token = self.stream.current
289*635a8641SAndroid Build Coastguard Worker        if token.type is TOKEN_EOF:
290*635a8641SAndroid Build Coastguard Worker            self.stream.close()
291*635a8641SAndroid Build Coastguard Worker            raise StopIteration()
292*635a8641SAndroid Build Coastguard Worker        next(self.stream)
293*635a8641SAndroid Build Coastguard Worker        return token
294*635a8641SAndroid Build Coastguard Worker
295*635a8641SAndroid Build Coastguard Worker
296*635a8641SAndroid Build Coastguard Worker@implements_iterator
297*635a8641SAndroid Build Coastguard Workerclass TokenStream(object):
298*635a8641SAndroid Build Coastguard Worker    """A token stream is an iterable that yields :class:`Token`\\s.  The
299*635a8641SAndroid Build Coastguard Worker    parser however does not iterate over it but calls :meth:`next` to go
300*635a8641SAndroid Build Coastguard Worker    one token ahead.  The current active token is stored as :attr:`current`.
301*635a8641SAndroid Build Coastguard Worker    """
302*635a8641SAndroid Build Coastguard Worker
303*635a8641SAndroid Build Coastguard Worker    def __init__(self, generator, name, filename):
304*635a8641SAndroid Build Coastguard Worker        self._iter = iter(generator)
305*635a8641SAndroid Build Coastguard Worker        self._pushed = deque()
306*635a8641SAndroid Build Coastguard Worker        self.name = name
307*635a8641SAndroid Build Coastguard Worker        self.filename = filename
308*635a8641SAndroid Build Coastguard Worker        self.closed = False
309*635a8641SAndroid Build Coastguard Worker        self.current = Token(1, TOKEN_INITIAL, '')
310*635a8641SAndroid Build Coastguard Worker        next(self)
311*635a8641SAndroid Build Coastguard Worker
312*635a8641SAndroid Build Coastguard Worker    def __iter__(self):
313*635a8641SAndroid Build Coastguard Worker        return TokenStreamIterator(self)
314*635a8641SAndroid Build Coastguard Worker
315*635a8641SAndroid Build Coastguard Worker    def __bool__(self):
316*635a8641SAndroid Build Coastguard Worker        return bool(self._pushed) or self.current.type is not TOKEN_EOF
317*635a8641SAndroid Build Coastguard Worker    __nonzero__ = __bool__  # py2
318*635a8641SAndroid Build Coastguard Worker
319*635a8641SAndroid Build Coastguard Worker    eos = property(lambda x: not x, doc="Are we at the end of the stream?")
320*635a8641SAndroid Build Coastguard Worker
321*635a8641SAndroid Build Coastguard Worker    def push(self, token):
322*635a8641SAndroid Build Coastguard Worker        """Push a token back to the stream."""
323*635a8641SAndroid Build Coastguard Worker        self._pushed.append(token)
324*635a8641SAndroid Build Coastguard Worker
325*635a8641SAndroid Build Coastguard Worker    def look(self):
326*635a8641SAndroid Build Coastguard Worker        """Look at the next token."""
327*635a8641SAndroid Build Coastguard Worker        old_token = next(self)
328*635a8641SAndroid Build Coastguard Worker        result = self.current
329*635a8641SAndroid Build Coastguard Worker        self.push(result)
330*635a8641SAndroid Build Coastguard Worker        self.current = old_token
331*635a8641SAndroid Build Coastguard Worker        return result
332*635a8641SAndroid Build Coastguard Worker
333*635a8641SAndroid Build Coastguard Worker    def skip(self, n=1):
334*635a8641SAndroid Build Coastguard Worker        """Got n tokens ahead."""
335*635a8641SAndroid Build Coastguard Worker        for x in range(n):
336*635a8641SAndroid Build Coastguard Worker            next(self)
337*635a8641SAndroid Build Coastguard Worker
338*635a8641SAndroid Build Coastguard Worker    def next_if(self, expr):
339*635a8641SAndroid Build Coastguard Worker        """Perform the token test and return the token if it matched.
340*635a8641SAndroid Build Coastguard Worker        Otherwise the return value is `None`.
341*635a8641SAndroid Build Coastguard Worker        """
342*635a8641SAndroid Build Coastguard Worker        if self.current.test(expr):
343*635a8641SAndroid Build Coastguard Worker            return next(self)
344*635a8641SAndroid Build Coastguard Worker
345*635a8641SAndroid Build Coastguard Worker    def skip_if(self, expr):
346*635a8641SAndroid Build Coastguard Worker        """Like :meth:`next_if` but only returns `True` or `False`."""
347*635a8641SAndroid Build Coastguard Worker        return self.next_if(expr) is not None
348*635a8641SAndroid Build Coastguard Worker
349*635a8641SAndroid Build Coastguard Worker    def __next__(self):
350*635a8641SAndroid Build Coastguard Worker        """Go one token ahead and return the old one.
351*635a8641SAndroid Build Coastguard Worker
352*635a8641SAndroid Build Coastguard Worker        Use the built-in :func:`next` instead of calling this directly.
353*635a8641SAndroid Build Coastguard Worker        """
354*635a8641SAndroid Build Coastguard Worker        rv = self.current
355*635a8641SAndroid Build Coastguard Worker        if self._pushed:
356*635a8641SAndroid Build Coastguard Worker            self.current = self._pushed.popleft()
357*635a8641SAndroid Build Coastguard Worker        elif self.current.type is not TOKEN_EOF:
358*635a8641SAndroid Build Coastguard Worker            try:
359*635a8641SAndroid Build Coastguard Worker                self.current = next(self._iter)
360*635a8641SAndroid Build Coastguard Worker            except StopIteration:
361*635a8641SAndroid Build Coastguard Worker                self.close()
362*635a8641SAndroid Build Coastguard Worker        return rv
363*635a8641SAndroid Build Coastguard Worker
364*635a8641SAndroid Build Coastguard Worker    def close(self):
365*635a8641SAndroid Build Coastguard Worker        """Close the stream."""
366*635a8641SAndroid Build Coastguard Worker        self.current = Token(self.current.lineno, TOKEN_EOF, '')
367*635a8641SAndroid Build Coastguard Worker        self._iter = None
368*635a8641SAndroid Build Coastguard Worker        self.closed = True
369*635a8641SAndroid Build Coastguard Worker
370*635a8641SAndroid Build Coastguard Worker    def expect(self, expr):
371*635a8641SAndroid Build Coastguard Worker        """Expect a given token type and return it.  This accepts the same
372*635a8641SAndroid Build Coastguard Worker        argument as :meth:`jinja2.lexer.Token.test`.
373*635a8641SAndroid Build Coastguard Worker        """
374*635a8641SAndroid Build Coastguard Worker        if not self.current.test(expr):
375*635a8641SAndroid Build Coastguard Worker            expr = describe_token_expr(expr)
376*635a8641SAndroid Build Coastguard Worker            if self.current.type is TOKEN_EOF:
377*635a8641SAndroid Build Coastguard Worker                raise TemplateSyntaxError('unexpected end of template, '
378*635a8641SAndroid Build Coastguard Worker                                          'expected %r.' % expr,
379*635a8641SAndroid Build Coastguard Worker                                          self.current.lineno,
380*635a8641SAndroid Build Coastguard Worker                                          self.name, self.filename)
381*635a8641SAndroid Build Coastguard Worker            raise TemplateSyntaxError("expected token %r, got %r" %
382*635a8641SAndroid Build Coastguard Worker                                      (expr, describe_token(self.current)),
383*635a8641SAndroid Build Coastguard Worker                                      self.current.lineno,
384*635a8641SAndroid Build Coastguard Worker                                      self.name, self.filename)
385*635a8641SAndroid Build Coastguard Worker        try:
386*635a8641SAndroid Build Coastguard Worker            return self.current
387*635a8641SAndroid Build Coastguard Worker        finally:
388*635a8641SAndroid Build Coastguard Worker            next(self)
389*635a8641SAndroid Build Coastguard Worker
390*635a8641SAndroid Build Coastguard Worker
391*635a8641SAndroid Build Coastguard Workerdef get_lexer(environment):
392*635a8641SAndroid Build Coastguard Worker    """Return a lexer which is probably cached."""
393*635a8641SAndroid Build Coastguard Worker    key = (environment.block_start_string,
394*635a8641SAndroid Build Coastguard Worker           environment.block_end_string,
395*635a8641SAndroid Build Coastguard Worker           environment.variable_start_string,
396*635a8641SAndroid Build Coastguard Worker           environment.variable_end_string,
397*635a8641SAndroid Build Coastguard Worker           environment.comment_start_string,
398*635a8641SAndroid Build Coastguard Worker           environment.comment_end_string,
399*635a8641SAndroid Build Coastguard Worker           environment.line_statement_prefix,
400*635a8641SAndroid Build Coastguard Worker           environment.line_comment_prefix,
401*635a8641SAndroid Build Coastguard Worker           environment.trim_blocks,
402*635a8641SAndroid Build Coastguard Worker           environment.lstrip_blocks,
403*635a8641SAndroid Build Coastguard Worker           environment.newline_sequence,
404*635a8641SAndroid Build Coastguard Worker           environment.keep_trailing_newline)
405*635a8641SAndroid Build Coastguard Worker    lexer = _lexer_cache.get(key)
406*635a8641SAndroid Build Coastguard Worker    if lexer is None:
407*635a8641SAndroid Build Coastguard Worker        lexer = Lexer(environment)
408*635a8641SAndroid Build Coastguard Worker        _lexer_cache[key] = lexer
409*635a8641SAndroid Build Coastguard Worker    return lexer
410*635a8641SAndroid Build Coastguard Worker
411*635a8641SAndroid Build Coastguard Worker
412*635a8641SAndroid Build Coastguard Workerclass Lexer(object):
413*635a8641SAndroid Build Coastguard Worker    """Class that implements a lexer for a given environment. Automatically
414*635a8641SAndroid Build Coastguard Worker    created by the environment class, usually you don't have to do that.
415*635a8641SAndroid Build Coastguard Worker
416*635a8641SAndroid Build Coastguard Worker    Note that the lexer is not automatically bound to an environment.
417*635a8641SAndroid Build Coastguard Worker    Multiple environments can share the same lexer.
418*635a8641SAndroid Build Coastguard Worker    """
419*635a8641SAndroid Build Coastguard Worker
420*635a8641SAndroid Build Coastguard Worker    def __init__(self, environment):
421*635a8641SAndroid Build Coastguard Worker        # shortcuts
422*635a8641SAndroid Build Coastguard Worker        c = lambda x: re.compile(x, re.M | re.S)
423*635a8641SAndroid Build Coastguard Worker        e = re.escape
424*635a8641SAndroid Build Coastguard Worker
425*635a8641SAndroid Build Coastguard Worker        # lexing rules for tags
426*635a8641SAndroid Build Coastguard Worker        tag_rules = [
427*635a8641SAndroid Build Coastguard Worker            (whitespace_re, TOKEN_WHITESPACE, None),
428*635a8641SAndroid Build Coastguard Worker            (float_re, TOKEN_FLOAT, None),
429*635a8641SAndroid Build Coastguard Worker            (integer_re, TOKEN_INTEGER, None),
430*635a8641SAndroid Build Coastguard Worker            (name_re, TOKEN_NAME, None),
431*635a8641SAndroid Build Coastguard Worker            (string_re, TOKEN_STRING, None),
432*635a8641SAndroid Build Coastguard Worker            (operator_re, TOKEN_OPERATOR, None)
433*635a8641SAndroid Build Coastguard Worker        ]
434*635a8641SAndroid Build Coastguard Worker
435*635a8641SAndroid Build Coastguard Worker        # assemble the root lexing rule. because "|" is ungreedy
436*635a8641SAndroid Build Coastguard Worker        # we have to sort by length so that the lexer continues working
437*635a8641SAndroid Build Coastguard Worker        # as expected when we have parsing rules like <% for block and
438*635a8641SAndroid Build Coastguard Worker        # <%= for variables. (if someone wants asp like syntax)
439*635a8641SAndroid Build Coastguard Worker        # variables are just part of the rules if variable processing
440*635a8641SAndroid Build Coastguard Worker        # is required.
441*635a8641SAndroid Build Coastguard Worker        root_tag_rules = compile_rules(environment)
442*635a8641SAndroid Build Coastguard Worker
443*635a8641SAndroid Build Coastguard Worker        # block suffix if trimming is enabled
444*635a8641SAndroid Build Coastguard Worker        block_suffix_re = environment.trim_blocks and '\\n?' or ''
445*635a8641SAndroid Build Coastguard Worker
446*635a8641SAndroid Build Coastguard Worker        # strip leading spaces if lstrip_blocks is enabled
447*635a8641SAndroid Build Coastguard Worker        prefix_re = {}
448*635a8641SAndroid Build Coastguard Worker        if environment.lstrip_blocks:
449*635a8641SAndroid Build Coastguard Worker            # use '{%+' to manually disable lstrip_blocks behavior
450*635a8641SAndroid Build Coastguard Worker            no_lstrip_re = e('+')
451*635a8641SAndroid Build Coastguard Worker            # detect overlap between block and variable or comment strings
452*635a8641SAndroid Build Coastguard Worker            block_diff = c(r'^%s(.*)' % e(environment.block_start_string))
453*635a8641SAndroid Build Coastguard Worker            # make sure we don't mistake a block for a variable or a comment
454*635a8641SAndroid Build Coastguard Worker            m = block_diff.match(environment.comment_start_string)
455*635a8641SAndroid Build Coastguard Worker            no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
456*635a8641SAndroid Build Coastguard Worker            m = block_diff.match(environment.variable_start_string)
457*635a8641SAndroid Build Coastguard Worker            no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
458*635a8641SAndroid Build Coastguard Worker
459*635a8641SAndroid Build Coastguard Worker            # detect overlap between comment and variable strings
460*635a8641SAndroid Build Coastguard Worker            comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string))
461*635a8641SAndroid Build Coastguard Worker            m = comment_diff.match(environment.variable_start_string)
462*635a8641SAndroid Build Coastguard Worker            no_variable_re = m and r'(?!%s)' % e(m.group(1)) or ''
463*635a8641SAndroid Build Coastguard Worker
464*635a8641SAndroid Build Coastguard Worker            lstrip_re = r'^[ \t]*'
465*635a8641SAndroid Build Coastguard Worker            block_prefix_re = r'%s%s(?!%s)|%s\+?' % (
466*635a8641SAndroid Build Coastguard Worker                    lstrip_re,
467*635a8641SAndroid Build Coastguard Worker                    e(environment.block_start_string),
468*635a8641SAndroid Build Coastguard Worker                    no_lstrip_re,
469*635a8641SAndroid Build Coastguard Worker                    e(environment.block_start_string),
470*635a8641SAndroid Build Coastguard Worker                    )
471*635a8641SAndroid Build Coastguard Worker            comment_prefix_re = r'%s%s%s|%s\+?' % (
472*635a8641SAndroid Build Coastguard Worker                    lstrip_re,
473*635a8641SAndroid Build Coastguard Worker                    e(environment.comment_start_string),
474*635a8641SAndroid Build Coastguard Worker                    no_variable_re,
475*635a8641SAndroid Build Coastguard Worker                    e(environment.comment_start_string),
476*635a8641SAndroid Build Coastguard Worker                    )
477*635a8641SAndroid Build Coastguard Worker            prefix_re['block'] = block_prefix_re
478*635a8641SAndroid Build Coastguard Worker            prefix_re['comment'] = comment_prefix_re
479*635a8641SAndroid Build Coastguard Worker        else:
480*635a8641SAndroid Build Coastguard Worker            block_prefix_re = '%s' % e(environment.block_start_string)
481*635a8641SAndroid Build Coastguard Worker
482*635a8641SAndroid Build Coastguard Worker        self.newline_sequence = environment.newline_sequence
483*635a8641SAndroid Build Coastguard Worker        self.keep_trailing_newline = environment.keep_trailing_newline
484*635a8641SAndroid Build Coastguard Worker
485*635a8641SAndroid Build Coastguard Worker        # global lexing rules
486*635a8641SAndroid Build Coastguard Worker        self.rules = {
487*635a8641SAndroid Build Coastguard Worker            'root': [
488*635a8641SAndroid Build Coastguard Worker                # directives
489*635a8641SAndroid Build Coastguard Worker                (c('(.*?)(?:%s)' % '|'.join(
490*635a8641SAndroid Build Coastguard Worker                    [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
491*635a8641SAndroid Build Coastguard Worker                        e(environment.block_start_string),
492*635a8641SAndroid Build Coastguard Worker                        block_prefix_re,
493*635a8641SAndroid Build Coastguard Worker                        e(environment.block_end_string),
494*635a8641SAndroid Build Coastguard Worker                        e(environment.block_end_string)
495*635a8641SAndroid Build Coastguard Worker                    )] + [
496*635a8641SAndroid Build Coastguard Worker                        r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r))
497*635a8641SAndroid Build Coastguard Worker                        for n, r in root_tag_rules
498*635a8641SAndroid Build Coastguard Worker                    ])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
499*635a8641SAndroid Build Coastguard Worker                # data
500*635a8641SAndroid Build Coastguard Worker                (c('.+'), TOKEN_DATA, None)
501*635a8641SAndroid Build Coastguard Worker            ],
502*635a8641SAndroid Build Coastguard Worker            # comments
503*635a8641SAndroid Build Coastguard Worker            TOKEN_COMMENT_BEGIN: [
504*635a8641SAndroid Build Coastguard Worker                (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
505*635a8641SAndroid Build Coastguard Worker                    e(environment.comment_end_string),
506*635a8641SAndroid Build Coastguard Worker                    e(environment.comment_end_string),
507*635a8641SAndroid Build Coastguard Worker                    block_suffix_re
508*635a8641SAndroid Build Coastguard Worker                )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
509*635a8641SAndroid Build Coastguard Worker                (c('(.)'), (Failure('Missing end of comment tag'),), None)
510*635a8641SAndroid Build Coastguard Worker            ],
511*635a8641SAndroid Build Coastguard Worker            # blocks
512*635a8641SAndroid Build Coastguard Worker            TOKEN_BLOCK_BEGIN: [
513*635a8641SAndroid Build Coastguard Worker                (c(r'(?:\-%s\s*|%s)%s' % (
514*635a8641SAndroid Build Coastguard Worker                    e(environment.block_end_string),
515*635a8641SAndroid Build Coastguard Worker                    e(environment.block_end_string),
516*635a8641SAndroid Build Coastguard Worker                    block_suffix_re
517*635a8641SAndroid Build Coastguard Worker                )), TOKEN_BLOCK_END, '#pop'),
518*635a8641SAndroid Build Coastguard Worker            ] + tag_rules,
519*635a8641SAndroid Build Coastguard Worker            # variables
520*635a8641SAndroid Build Coastguard Worker            TOKEN_VARIABLE_BEGIN: [
521*635a8641SAndroid Build Coastguard Worker                (c(r'\-%s\s*|%s' % (
522*635a8641SAndroid Build Coastguard Worker                    e(environment.variable_end_string),
523*635a8641SAndroid Build Coastguard Worker                    e(environment.variable_end_string)
524*635a8641SAndroid Build Coastguard Worker                )), TOKEN_VARIABLE_END, '#pop')
525*635a8641SAndroid Build Coastguard Worker            ] + tag_rules,
526*635a8641SAndroid Build Coastguard Worker            # raw block
527*635a8641SAndroid Build Coastguard Worker            TOKEN_RAW_BEGIN: [
528*635a8641SAndroid Build Coastguard Worker                (c(r'(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
529*635a8641SAndroid Build Coastguard Worker                    e(environment.block_start_string),
530*635a8641SAndroid Build Coastguard Worker                    block_prefix_re,
531*635a8641SAndroid Build Coastguard Worker                    e(environment.block_end_string),
532*635a8641SAndroid Build Coastguard Worker                    e(environment.block_end_string),
533*635a8641SAndroid Build Coastguard Worker                    block_suffix_re
534*635a8641SAndroid Build Coastguard Worker                )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
535*635a8641SAndroid Build Coastguard Worker                (c('(.)'), (Failure('Missing end of raw directive'),), None)
536*635a8641SAndroid Build Coastguard Worker            ],
537*635a8641SAndroid Build Coastguard Worker            # line statements
538*635a8641SAndroid Build Coastguard Worker            TOKEN_LINESTATEMENT_BEGIN: [
539*635a8641SAndroid Build Coastguard Worker                (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
540*635a8641SAndroid Build Coastguard Worker            ] + tag_rules,
541*635a8641SAndroid Build Coastguard Worker            # line comments
542*635a8641SAndroid Build Coastguard Worker            TOKEN_LINECOMMENT_BEGIN: [
543*635a8641SAndroid Build Coastguard Worker                (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
544*635a8641SAndroid Build Coastguard Worker                 TOKEN_LINECOMMENT_END), '#pop')
545*635a8641SAndroid Build Coastguard Worker            ]
546*635a8641SAndroid Build Coastguard Worker        }
547*635a8641SAndroid Build Coastguard Worker
548*635a8641SAndroid Build Coastguard Worker    def _normalize_newlines(self, value):
549*635a8641SAndroid Build Coastguard Worker        """Called for strings and template data to normalize it to unicode."""
550*635a8641SAndroid Build Coastguard Worker        return newline_re.sub(self.newline_sequence, value)
551*635a8641SAndroid Build Coastguard Worker
552*635a8641SAndroid Build Coastguard Worker    def tokenize(self, source, name=None, filename=None, state=None):
553*635a8641SAndroid Build Coastguard Worker        """Calls tokeniter + tokenize and wraps it in a token stream.
554*635a8641SAndroid Build Coastguard Worker        """
555*635a8641SAndroid Build Coastguard Worker        stream = self.tokeniter(source, name, filename, state)
556*635a8641SAndroid Build Coastguard Worker        return TokenStream(self.wrap(stream, name, filename), name, filename)
557*635a8641SAndroid Build Coastguard Worker
558*635a8641SAndroid Build Coastguard Worker    def wrap(self, stream, name=None, filename=None):
559*635a8641SAndroid Build Coastguard Worker        """This is called with the stream as returned by `tokenize` and wraps
560*635a8641SAndroid Build Coastguard Worker        every token in a :class:`Token` and converts the value.
561*635a8641SAndroid Build Coastguard Worker        """
562*635a8641SAndroid Build Coastguard Worker        for lineno, token, value in stream:
563*635a8641SAndroid Build Coastguard Worker            if token in ignored_tokens:
564*635a8641SAndroid Build Coastguard Worker                continue
565*635a8641SAndroid Build Coastguard Worker            elif token == 'linestatement_begin':
566*635a8641SAndroid Build Coastguard Worker                token = 'block_begin'
567*635a8641SAndroid Build Coastguard Worker            elif token == 'linestatement_end':
568*635a8641SAndroid Build Coastguard Worker                token = 'block_end'
569*635a8641SAndroid Build Coastguard Worker            # we are not interested in those tokens in the parser
570*635a8641SAndroid Build Coastguard Worker            elif token in ('raw_begin', 'raw_end'):
571*635a8641SAndroid Build Coastguard Worker                continue
572*635a8641SAndroid Build Coastguard Worker            elif token == 'data':
573*635a8641SAndroid Build Coastguard Worker                value = self._normalize_newlines(value)
574*635a8641SAndroid Build Coastguard Worker            elif token == 'keyword':
575*635a8641SAndroid Build Coastguard Worker                token = value
576*635a8641SAndroid Build Coastguard Worker            elif token == 'name':
577*635a8641SAndroid Build Coastguard Worker                value = str(value)
578*635a8641SAndroid Build Coastguard Worker                if check_ident and not value.isidentifier():
579*635a8641SAndroid Build Coastguard Worker                    raise TemplateSyntaxError(
580*635a8641SAndroid Build Coastguard Worker                        'Invalid character in identifier',
581*635a8641SAndroid Build Coastguard Worker                        lineno, name, filename)
582*635a8641SAndroid Build Coastguard Worker            elif token == 'string':
583*635a8641SAndroid Build Coastguard Worker                # try to unescape string
584*635a8641SAndroid Build Coastguard Worker                try:
585*635a8641SAndroid Build Coastguard Worker                    value = self._normalize_newlines(value[1:-1]) \
586*635a8641SAndroid Build Coastguard Worker                        .encode('ascii', 'backslashreplace') \
587*635a8641SAndroid Build Coastguard Worker                        .decode('unicode-escape')
588*635a8641SAndroid Build Coastguard Worker                except Exception as e:
589*635a8641SAndroid Build Coastguard Worker                    msg = str(e).split(':')[-1].strip()
590*635a8641SAndroid Build Coastguard Worker                    raise TemplateSyntaxError(msg, lineno, name, filename)
591*635a8641SAndroid Build Coastguard Worker            elif token == 'integer':
592*635a8641SAndroid Build Coastguard Worker                value = int(value)
593*635a8641SAndroid Build Coastguard Worker            elif token == 'float':
594*635a8641SAndroid Build Coastguard Worker                value = float(value)
595*635a8641SAndroid Build Coastguard Worker            elif token == 'operator':
596*635a8641SAndroid Build Coastguard Worker                token = operators[value]
597*635a8641SAndroid Build Coastguard Worker            yield Token(lineno, token, value)
598*635a8641SAndroid Build Coastguard Worker
599*635a8641SAndroid Build Coastguard Worker    def tokeniter(self, source, name, filename=None, state=None):
600*635a8641SAndroid Build Coastguard Worker        """This method tokenizes the text and returns the tokens in a
601*635a8641SAndroid Build Coastguard Worker        generator.  Use this method if you just want to tokenize a template.
602*635a8641SAndroid Build Coastguard Worker        """
603*635a8641SAndroid Build Coastguard Worker        source = text_type(source)
604*635a8641SAndroid Build Coastguard Worker        lines = source.splitlines()
605*635a8641SAndroid Build Coastguard Worker        if self.keep_trailing_newline and source:
606*635a8641SAndroid Build Coastguard Worker            for newline in ('\r\n', '\r', '\n'):
607*635a8641SAndroid Build Coastguard Worker                if source.endswith(newline):
608*635a8641SAndroid Build Coastguard Worker                    lines.append('')
609*635a8641SAndroid Build Coastguard Worker                    break
610*635a8641SAndroid Build Coastguard Worker        source = '\n'.join(lines)
611*635a8641SAndroid Build Coastguard Worker        pos = 0
612*635a8641SAndroid Build Coastguard Worker        lineno = 1
613*635a8641SAndroid Build Coastguard Worker        stack = ['root']
614*635a8641SAndroid Build Coastguard Worker        if state is not None and state != 'root':
615*635a8641SAndroid Build Coastguard Worker            assert state in ('variable', 'block'), 'invalid state'
616*635a8641SAndroid Build Coastguard Worker            stack.append(state + '_begin')
617*635a8641SAndroid Build Coastguard Worker        else:
618*635a8641SAndroid Build Coastguard Worker            state = 'root'
619*635a8641SAndroid Build Coastguard Worker        statetokens = self.rules[stack[-1]]
620*635a8641SAndroid Build Coastguard Worker        source_length = len(source)
621*635a8641SAndroid Build Coastguard Worker
622*635a8641SAndroid Build Coastguard Worker        balancing_stack = []
623*635a8641SAndroid Build Coastguard Worker
624*635a8641SAndroid Build Coastguard Worker        while 1:
625*635a8641SAndroid Build Coastguard Worker            # tokenizer loop
626*635a8641SAndroid Build Coastguard Worker            for regex, tokens, new_state in statetokens:
627*635a8641SAndroid Build Coastguard Worker                m = regex.match(source, pos)
628*635a8641SAndroid Build Coastguard Worker                # if no match we try again with the next rule
629*635a8641SAndroid Build Coastguard Worker                if m is None:
630*635a8641SAndroid Build Coastguard Worker                    continue
631*635a8641SAndroid Build Coastguard Worker
632*635a8641SAndroid Build Coastguard Worker                # we only match blocks and variables if braces / parentheses
633*635a8641SAndroid Build Coastguard Worker                # are balanced. continue parsing with the lower rule which
634*635a8641SAndroid Build Coastguard Worker                # is the operator rule. do this only if the end tags look
635*635a8641SAndroid Build Coastguard Worker                # like operators
636*635a8641SAndroid Build Coastguard Worker                if balancing_stack and \
637*635a8641SAndroid Build Coastguard Worker                   tokens in ('variable_end', 'block_end',
638*635a8641SAndroid Build Coastguard Worker                              'linestatement_end'):
639*635a8641SAndroid Build Coastguard Worker                    continue
640*635a8641SAndroid Build Coastguard Worker
641*635a8641SAndroid Build Coastguard Worker                # tuples support more options
642*635a8641SAndroid Build Coastguard Worker                if isinstance(tokens, tuple):
643*635a8641SAndroid Build Coastguard Worker                    for idx, token in enumerate(tokens):
644*635a8641SAndroid Build Coastguard Worker                        # failure group
645*635a8641SAndroid Build Coastguard Worker                        if token.__class__ is Failure:
646*635a8641SAndroid Build Coastguard Worker                            raise token(lineno, filename)
647*635a8641SAndroid Build Coastguard Worker                        # bygroup is a bit more complex, in that case we
648*635a8641SAndroid Build Coastguard Worker                        # yield for the current token the first named
649*635a8641SAndroid Build Coastguard Worker                        # group that matched
650*635a8641SAndroid Build Coastguard Worker                        elif token == '#bygroup':
651*635a8641SAndroid Build Coastguard Worker                            for key, value in iteritems(m.groupdict()):
652*635a8641SAndroid Build Coastguard Worker                                if value is not None:
653*635a8641SAndroid Build Coastguard Worker                                    yield lineno, key, value
654*635a8641SAndroid Build Coastguard Worker                                    lineno += value.count('\n')
655*635a8641SAndroid Build Coastguard Worker                                    break
656*635a8641SAndroid Build Coastguard Worker                            else:
657*635a8641SAndroid Build Coastguard Worker                                raise RuntimeError('%r wanted to resolve '
658*635a8641SAndroid Build Coastguard Worker                                                   'the token dynamically'
659*635a8641SAndroid Build Coastguard Worker                                                   ' but no group matched'
660*635a8641SAndroid Build Coastguard Worker                                                   % regex)
661*635a8641SAndroid Build Coastguard Worker                        # normal group
662*635a8641SAndroid Build Coastguard Worker                        else:
663*635a8641SAndroid Build Coastguard Worker                            data = m.group(idx + 1)
664*635a8641SAndroid Build Coastguard Worker                            if data or token not in ignore_if_empty:
665*635a8641SAndroid Build Coastguard Worker                                yield lineno, token, data
666*635a8641SAndroid Build Coastguard Worker                            lineno += data.count('\n')
667*635a8641SAndroid Build Coastguard Worker
668*635a8641SAndroid Build Coastguard Worker                # strings as token just are yielded as it.
669*635a8641SAndroid Build Coastguard Worker                else:
670*635a8641SAndroid Build Coastguard Worker                    data = m.group()
671*635a8641SAndroid Build Coastguard Worker                    # update brace/parentheses balance
672*635a8641SAndroid Build Coastguard Worker                    if tokens == 'operator':
673*635a8641SAndroid Build Coastguard Worker                        if data == '{':
674*635a8641SAndroid Build Coastguard Worker                            balancing_stack.append('}')
675*635a8641SAndroid Build Coastguard Worker                        elif data == '(':
676*635a8641SAndroid Build Coastguard Worker                            balancing_stack.append(')')
677*635a8641SAndroid Build Coastguard Worker                        elif data == '[':
678*635a8641SAndroid Build Coastguard Worker                            balancing_stack.append(']')
679*635a8641SAndroid Build Coastguard Worker                        elif data in ('}', ')', ']'):
680*635a8641SAndroid Build Coastguard Worker                            if not balancing_stack:
681*635a8641SAndroid Build Coastguard Worker                                raise TemplateSyntaxError('unexpected \'%s\'' %
682*635a8641SAndroid Build Coastguard Worker                                                          data, lineno, name,
683*635a8641SAndroid Build Coastguard Worker                                                          filename)
684*635a8641SAndroid Build Coastguard Worker                            expected_op = balancing_stack.pop()
685*635a8641SAndroid Build Coastguard Worker                            if expected_op != data:
686*635a8641SAndroid Build Coastguard Worker                                raise TemplateSyntaxError('unexpected \'%s\', '
687*635a8641SAndroid Build Coastguard Worker                                                          'expected \'%s\'' %
688*635a8641SAndroid Build Coastguard Worker                                                          (data, expected_op),
689*635a8641SAndroid Build Coastguard Worker                                                          lineno, name,
690*635a8641SAndroid Build Coastguard Worker                                                          filename)
691*635a8641SAndroid Build Coastguard Worker                    # yield items
692*635a8641SAndroid Build Coastguard Worker                    if data or tokens not in ignore_if_empty:
693*635a8641SAndroid Build Coastguard Worker                        yield lineno, tokens, data
694*635a8641SAndroid Build Coastguard Worker                    lineno += data.count('\n')
695*635a8641SAndroid Build Coastguard Worker
696*635a8641SAndroid Build Coastguard Worker                # fetch new position into new variable so that we can check
697*635a8641SAndroid Build Coastguard Worker                # if there is a internal parsing error which would result
698*635a8641SAndroid Build Coastguard Worker                # in an infinite loop
699*635a8641SAndroid Build Coastguard Worker                pos2 = m.end()
700*635a8641SAndroid Build Coastguard Worker
701*635a8641SAndroid Build Coastguard Worker                # handle state changes
702*635a8641SAndroid Build Coastguard Worker                if new_state is not None:
703*635a8641SAndroid Build Coastguard Worker                    # remove the uppermost state
704*635a8641SAndroid Build Coastguard Worker                    if new_state == '#pop':
705*635a8641SAndroid Build Coastguard Worker                        stack.pop()
706*635a8641SAndroid Build Coastguard Worker                    # resolve the new state by group checking
707*635a8641SAndroid Build Coastguard Worker                    elif new_state == '#bygroup':
708*635a8641SAndroid Build Coastguard Worker                        for key, value in iteritems(m.groupdict()):
709*635a8641SAndroid Build Coastguard Worker                            if value is not None:
710*635a8641SAndroid Build Coastguard Worker                                stack.append(key)
711*635a8641SAndroid Build Coastguard Worker                                break
712*635a8641SAndroid Build Coastguard Worker                        else:
713*635a8641SAndroid Build Coastguard Worker                            raise RuntimeError('%r wanted to resolve the '
714*635a8641SAndroid Build Coastguard Worker                                               'new state dynamically but'
715*635a8641SAndroid Build Coastguard Worker                                               ' no group matched' %
716*635a8641SAndroid Build Coastguard Worker                                               regex)
717*635a8641SAndroid Build Coastguard Worker                    # direct state name given
718*635a8641SAndroid Build Coastguard Worker                    else:
719*635a8641SAndroid Build Coastguard Worker                        stack.append(new_state)
720*635a8641SAndroid Build Coastguard Worker                    statetokens = self.rules[stack[-1]]
721*635a8641SAndroid Build Coastguard Worker                # we are still at the same position and no stack change.
722*635a8641SAndroid Build Coastguard Worker                # this means a loop without break condition, avoid that and
723*635a8641SAndroid Build Coastguard Worker                # raise error
724*635a8641SAndroid Build Coastguard Worker                elif pos2 == pos:
725*635a8641SAndroid Build Coastguard Worker                    raise RuntimeError('%r yielded empty string without '
726*635a8641SAndroid Build Coastguard Worker                                       'stack change' % regex)
727*635a8641SAndroid Build Coastguard Worker                # publish new function and start again
728*635a8641SAndroid Build Coastguard Worker                pos = pos2
729*635a8641SAndroid Build Coastguard Worker                break
730*635a8641SAndroid Build Coastguard Worker            # if loop terminated without break we haven't found a single match
731*635a8641SAndroid Build Coastguard Worker            # either we are at the end of the file or we have a problem
732*635a8641SAndroid Build Coastguard Worker            else:
733*635a8641SAndroid Build Coastguard Worker                # end of text
734*635a8641SAndroid Build Coastguard Worker                if pos >= source_length:
735*635a8641SAndroid Build Coastguard Worker                    return
736*635a8641SAndroid Build Coastguard Worker                # something went wrong
737*635a8641SAndroid Build Coastguard Worker                raise TemplateSyntaxError('unexpected char %r at %d' %
738*635a8641SAndroid Build Coastguard Worker                                          (source[pos], pos), lineno,
739*635a8641SAndroid Build Coastguard Worker                                          name, filename)
740