| Index: mojo/public/third_party/jinja2/lexer.py
|
| diff --git a/mojo/public/third_party/jinja2/lexer.py b/mojo/public/third_party/jinja2/lexer.py
|
| deleted file mode 100644
|
| index a50128507bb98ac6bc57a76afe8a0776a2df2c49..0000000000000000000000000000000000000000
|
| --- a/mojo/public/third_party/jinja2/lexer.py
|
| +++ /dev/null
|
| @@ -1,733 +0,0 @@
|
| -# -*- coding: utf-8 -*-
|
| -"""
|
| - jinja2.lexer
|
| - ~~~~~~~~~~~~
|
| -
|
| - This module implements a Jinja / Python combination lexer. The
|
| - `Lexer` class provided by this module is used to do some preprocessing
|
| - for Jinja.
|
| -
|
| - On the one hand it filters out invalid operators like the bitshift
|
| - operators we don't allow in templates. On the other hand it separates
|
| - template code and python code in expressions.
|
| -
|
| - :copyright: (c) 2010 by the Jinja Team.
|
| - :license: BSD, see LICENSE for more details.
|
| -"""
|
| -import re
|
| -
|
| -from operator import itemgetter
|
| -from collections import deque
|
| -from jinja2.exceptions import TemplateSyntaxError
|
| -from jinja2.utils import LRUCache
|
| -from jinja2._compat import next, iteritems, implements_iterator, text_type, \
|
| - intern
|
| -
|
| -
|
| -# cache for the lexers. Exists in order to be able to have multiple
|
| -# environments with the same lexer
|
| -_lexer_cache = LRUCache(50)
|
| -
|
| -# static regular expressions
|
| -whitespace_re = re.compile(r'\s+', re.U)
|
| -string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
|
| - r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
|
| -integer_re = re.compile(r'\d+')
|
| -
|
| -# we use the unicode identifier rule if this python version is able
|
| -# to handle unicode identifiers, otherwise the standard ASCII one.
|
| -try:
|
| - compile('föö', '<unknown>', 'eval')
|
| -except SyntaxError:
|
| - name_re = re.compile(r'\b[a-zA-Z_][a-zA-Z0-9_]*\b')
|
| -else:
|
| - from jinja2 import _stringdefs
|
| - name_re = re.compile(r'[%s][%s]*' % (_stringdefs.xid_start,
|
| - _stringdefs.xid_continue))
|
| -
|
| -float_re = re.compile(r'(?<!\.)\d+\.\d+')
|
| -newline_re = re.compile(r'(\r\n|\r|\n)')
|
| -
|
| -# internal the tokens and keep references to them
|
| -TOKEN_ADD = intern('add')
|
| -TOKEN_ASSIGN = intern('assign')
|
| -TOKEN_COLON = intern('colon')
|
| -TOKEN_COMMA = intern('comma')
|
| -TOKEN_DIV = intern('div')
|
| -TOKEN_DOT = intern('dot')
|
| -TOKEN_EQ = intern('eq')
|
| -TOKEN_FLOORDIV = intern('floordiv')
|
| -TOKEN_GT = intern('gt')
|
| -TOKEN_GTEQ = intern('gteq')
|
| -TOKEN_LBRACE = intern('lbrace')
|
| -TOKEN_LBRACKET = intern('lbracket')
|
| -TOKEN_LPAREN = intern('lparen')
|
| -TOKEN_LT = intern('lt')
|
| -TOKEN_LTEQ = intern('lteq')
|
| -TOKEN_MOD = intern('mod')
|
| -TOKEN_MUL = intern('mul')
|
| -TOKEN_NE = intern('ne')
|
| -TOKEN_PIPE = intern('pipe')
|
| -TOKEN_POW = intern('pow')
|
| -TOKEN_RBRACE = intern('rbrace')
|
| -TOKEN_RBRACKET = intern('rbracket')
|
| -TOKEN_RPAREN = intern('rparen')
|
| -TOKEN_SEMICOLON = intern('semicolon')
|
| -TOKEN_SUB = intern('sub')
|
| -TOKEN_TILDE = intern('tilde')
|
| -TOKEN_WHITESPACE = intern('whitespace')
|
| -TOKEN_FLOAT = intern('float')
|
| -TOKEN_INTEGER = intern('integer')
|
| -TOKEN_NAME = intern('name')
|
| -TOKEN_STRING = intern('string')
|
| -TOKEN_OPERATOR = intern('operator')
|
| -TOKEN_BLOCK_BEGIN = intern('block_begin')
|
| -TOKEN_BLOCK_END = intern('block_end')
|
| -TOKEN_VARIABLE_BEGIN = intern('variable_begin')
|
| -TOKEN_VARIABLE_END = intern('variable_end')
|
| -TOKEN_RAW_BEGIN = intern('raw_begin')
|
| -TOKEN_RAW_END = intern('raw_end')
|
| -TOKEN_COMMENT_BEGIN = intern('comment_begin')
|
| -TOKEN_COMMENT_END = intern('comment_end')
|
| -TOKEN_COMMENT = intern('comment')
|
| -TOKEN_LINESTATEMENT_BEGIN = intern('linestatement_begin')
|
| -TOKEN_LINESTATEMENT_END = intern('linestatement_end')
|
| -TOKEN_LINECOMMENT_BEGIN = intern('linecomment_begin')
|
| -TOKEN_LINECOMMENT_END = intern('linecomment_end')
|
| -TOKEN_LINECOMMENT = intern('linecomment')
|
| -TOKEN_DATA = intern('data')
|
| -TOKEN_INITIAL = intern('initial')
|
| -TOKEN_EOF = intern('eof')
|
| -
|
| -# bind operators to token types
|
| -operators = {
|
| - '+': TOKEN_ADD,
|
| - '-': TOKEN_SUB,
|
| - '/': TOKEN_DIV,
|
| - '//': TOKEN_FLOORDIV,
|
| - '*': TOKEN_MUL,
|
| - '%': TOKEN_MOD,
|
| - '**': TOKEN_POW,
|
| - '~': TOKEN_TILDE,
|
| - '[': TOKEN_LBRACKET,
|
| - ']': TOKEN_RBRACKET,
|
| - '(': TOKEN_LPAREN,
|
| - ')': TOKEN_RPAREN,
|
| - '{': TOKEN_LBRACE,
|
| - '}': TOKEN_RBRACE,
|
| - '==': TOKEN_EQ,
|
| - '!=': TOKEN_NE,
|
| - '>': TOKEN_GT,
|
| - '>=': TOKEN_GTEQ,
|
| - '<': TOKEN_LT,
|
| - '<=': TOKEN_LTEQ,
|
| - '=': TOKEN_ASSIGN,
|
| - '.': TOKEN_DOT,
|
| - ':': TOKEN_COLON,
|
| - '|': TOKEN_PIPE,
|
| - ',': TOKEN_COMMA,
|
| - ';': TOKEN_SEMICOLON
|
| -}
|
| -
|
| -reverse_operators = dict([(v, k) for k, v in iteritems(operators)])
|
| -assert len(operators) == len(reverse_operators), 'operators dropped'
|
| -operator_re = re.compile('(%s)' % '|'.join(re.escape(x) for x in
|
| - sorted(operators, key=lambda x: -len(x))))
|
| -
|
| -ignored_tokens = frozenset([TOKEN_COMMENT_BEGIN, TOKEN_COMMENT,
|
| - TOKEN_COMMENT_END, TOKEN_WHITESPACE,
|
| - TOKEN_WHITESPACE, TOKEN_LINECOMMENT_BEGIN,
|
| - TOKEN_LINECOMMENT_END, TOKEN_LINECOMMENT])
|
| -ignore_if_empty = frozenset([TOKEN_WHITESPACE, TOKEN_DATA,
|
| - TOKEN_COMMENT, TOKEN_LINECOMMENT])
|
| -
|
| -
|
| -def _describe_token_type(token_type):
|
| - if token_type in reverse_operators:
|
| - return reverse_operators[token_type]
|
| - return {
|
| - TOKEN_COMMENT_BEGIN: 'begin of comment',
|
| - TOKEN_COMMENT_END: 'end of comment',
|
| - TOKEN_COMMENT: 'comment',
|
| - TOKEN_LINECOMMENT: 'comment',
|
| - TOKEN_BLOCK_BEGIN: 'begin of statement block',
|
| - TOKEN_BLOCK_END: 'end of statement block',
|
| - TOKEN_VARIABLE_BEGIN: 'begin of print statement',
|
| - TOKEN_VARIABLE_END: 'end of print statement',
|
| - TOKEN_LINESTATEMENT_BEGIN: 'begin of line statement',
|
| - TOKEN_LINESTATEMENT_END: 'end of line statement',
|
| - TOKEN_DATA: 'template data / text',
|
| - TOKEN_EOF: 'end of template'
|
| - }.get(token_type, token_type)
|
| -
|
| -
|
| -def describe_token(token):
|
| - """Returns a description of the token."""
|
| - if token.type == 'name':
|
| - return token.value
|
| - return _describe_token_type(token.type)
|
| -
|
| -
|
| -def describe_token_expr(expr):
|
| - """Like `describe_token` but for token expressions."""
|
| - if ':' in expr:
|
| - type, value = expr.split(':', 1)
|
| - if type == 'name':
|
| - return value
|
| - else:
|
| - type = expr
|
| - return _describe_token_type(type)
|
| -
|
| -
|
| -def count_newlines(value):
|
| - """Count the number of newline characters in the string. This is
|
| - useful for extensions that filter a stream.
|
| - """
|
| - return len(newline_re.findall(value))
|
| -
|
| -
|
| -def compile_rules(environment):
|
| - """Compiles all the rules from the environment into a list of rules."""
|
| - e = re.escape
|
| - rules = [
|
| - (len(environment.comment_start_string), 'comment',
|
| - e(environment.comment_start_string)),
|
| - (len(environment.block_start_string), 'block',
|
| - e(environment.block_start_string)),
|
| - (len(environment.variable_start_string), 'variable',
|
| - e(environment.variable_start_string))
|
| - ]
|
| -
|
| - if environment.line_statement_prefix is not None:
|
| - rules.append((len(environment.line_statement_prefix), 'linestatement',
|
| - r'^[ \t\v]*' + e(environment.line_statement_prefix)))
|
| - if environment.line_comment_prefix is not None:
|
| - rules.append((len(environment.line_comment_prefix), 'linecomment',
|
| - r'(?:^|(?<=\S))[^\S\r\n]*' +
|
| - e(environment.line_comment_prefix)))
|
| -
|
| - return [x[1:] for x in sorted(rules, reverse=True)]
|
| -
|
| -
|
| -class Failure(object):
|
| - """Class that raises a `TemplateSyntaxError` if called.
|
| - Used by the `Lexer` to specify known errors.
|
| - """
|
| -
|
| - def __init__(self, message, cls=TemplateSyntaxError):
|
| - self.message = message
|
| - self.error_class = cls
|
| -
|
| - def __call__(self, lineno, filename):
|
| - raise self.error_class(self.message, lineno, filename)
|
| -
|
| -
|
| -class Token(tuple):
|
| - """Token class."""
|
| - __slots__ = ()
|
| - lineno, type, value = (property(itemgetter(x)) for x in range(3))
|
| -
|
| - def __new__(cls, lineno, type, value):
|
| - return tuple.__new__(cls, (lineno, intern(str(type)), value))
|
| -
|
| - def __str__(self):
|
| - if self.type in reverse_operators:
|
| - return reverse_operators[self.type]
|
| - elif self.type == 'name':
|
| - return self.value
|
| - return self.type
|
| -
|
| - def test(self, expr):
|
| - """Test a token against a token expression. This can either be a
|
| - token type or ``'token_type:token_value'``. This can only test
|
| - against string values and types.
|
| - """
|
| - # here we do a regular string equality check as test_any is usually
|
| - # passed an iterable of not interned strings.
|
| - if self.type == expr:
|
| - return True
|
| - elif ':' in expr:
|
| - return expr.split(':', 1) == [self.type, self.value]
|
| - return False
|
| -
|
| - def test_any(self, *iterable):
|
| - """Test against multiple token expressions."""
|
| - for expr in iterable:
|
| - if self.test(expr):
|
| - return True
|
| - return False
|
| -
|
| - def __repr__(self):
|
| - return 'Token(%r, %r, %r)' % (
|
| - self.lineno,
|
| - self.type,
|
| - self.value
|
| - )
|
| -
|
| -
|
| -@implements_iterator
|
| -class TokenStreamIterator(object):
|
| - """The iterator for tokenstreams. Iterate over the stream
|
| - until the eof token is reached.
|
| - """
|
| -
|
| - def __init__(self, stream):
|
| - self.stream = stream
|
| -
|
| - def __iter__(self):
|
| - return self
|
| -
|
| - def __next__(self):
|
| - token = self.stream.current
|
| - if token.type is TOKEN_EOF:
|
| - self.stream.close()
|
| - raise StopIteration()
|
| - next(self.stream)
|
| - return token
|
| -
|
| -
|
| -@implements_iterator
|
| -class TokenStream(object):
|
| - """A token stream is an iterable that yields :class:`Token`\s. The
|
| - parser however does not iterate over it but calls :meth:`next` to go
|
| - one token ahead. The current active token is stored as :attr:`current`.
|
| - """
|
| -
|
| - def __init__(self, generator, name, filename):
|
| - self._iter = iter(generator)
|
| - self._pushed = deque()
|
| - self.name = name
|
| - self.filename = filename
|
| - self.closed = False
|
| - self.current = Token(1, TOKEN_INITIAL, '')
|
| - next(self)
|
| -
|
| - def __iter__(self):
|
| - return TokenStreamIterator(self)
|
| -
|
| - def __bool__(self):
|
| - return bool(self._pushed) or self.current.type is not TOKEN_EOF
|
| - __nonzero__ = __bool__ # py2
|
| -
|
| - eos = property(lambda x: not x, doc="Are we at the end of the stream?")
|
| -
|
| - def push(self, token):
|
| - """Push a token back to the stream."""
|
| - self._pushed.append(token)
|
| -
|
| - def look(self):
|
| - """Look at the next token."""
|
| - old_token = next(self)
|
| - result = self.current
|
| - self.push(result)
|
| - self.current = old_token
|
| - return result
|
| -
|
| - def skip(self, n=1):
|
| - """Got n tokens ahead."""
|
| - for x in range(n):
|
| - next(self)
|
| -
|
| - def next_if(self, expr):
|
| - """Perform the token test and return the token if it matched.
|
| - Otherwise the return value is `None`.
|
| - """
|
| - if self.current.test(expr):
|
| - return next(self)
|
| -
|
| - def skip_if(self, expr):
|
| - """Like :meth:`next_if` but only returns `True` or `False`."""
|
| - return self.next_if(expr) is not None
|
| -
|
| - def __next__(self):
|
| - """Go one token ahead and return the old one"""
|
| - rv = self.current
|
| - if self._pushed:
|
| - self.current = self._pushed.popleft()
|
| - elif self.current.type is not TOKEN_EOF:
|
| - try:
|
| - self.current = next(self._iter)
|
| - except StopIteration:
|
| - self.close()
|
| - return rv
|
| -
|
| - def close(self):
|
| - """Close the stream."""
|
| - self.current = Token(self.current.lineno, TOKEN_EOF, '')
|
| - self._iter = None
|
| - self.closed = True
|
| -
|
| - def expect(self, expr):
|
| - """Expect a given token type and return it. This accepts the same
|
| - argument as :meth:`jinja2.lexer.Token.test`.
|
| - """
|
| - if not self.current.test(expr):
|
| - expr = describe_token_expr(expr)
|
| - if self.current.type is TOKEN_EOF:
|
| - raise TemplateSyntaxError('unexpected end of template, '
|
| - 'expected %r.' % expr,
|
| - self.current.lineno,
|
| - self.name, self.filename)
|
| - raise TemplateSyntaxError("expected token %r, got %r" %
|
| - (expr, describe_token(self.current)),
|
| - self.current.lineno,
|
| - self.name, self.filename)
|
| - try:
|
| - return self.current
|
| - finally:
|
| - next(self)
|
| -
|
| -
|
| -def get_lexer(environment):
|
| - """Return a lexer which is probably cached."""
|
| - key = (environment.block_start_string,
|
| - environment.block_end_string,
|
| - environment.variable_start_string,
|
| - environment.variable_end_string,
|
| - environment.comment_start_string,
|
| - environment.comment_end_string,
|
| - environment.line_statement_prefix,
|
| - environment.line_comment_prefix,
|
| - environment.trim_blocks,
|
| - environment.lstrip_blocks,
|
| - environment.newline_sequence,
|
| - environment.keep_trailing_newline)
|
| - lexer = _lexer_cache.get(key)
|
| - if lexer is None:
|
| - lexer = Lexer(environment)
|
| - _lexer_cache[key] = lexer
|
| - return lexer
|
| -
|
| -
|
| -class Lexer(object):
|
| - """Class that implements a lexer for a given environment. Automatically
|
| - created by the environment class, usually you don't have to do that.
|
| -
|
| - Note that the lexer is not automatically bound to an environment.
|
| - Multiple environments can share the same lexer.
|
| - """
|
| -
|
| - def __init__(self, environment):
|
| - # shortcuts
|
| - c = lambda x: re.compile(x, re.M | re.S)
|
| - e = re.escape
|
| -
|
| - # lexing rules for tags
|
| - tag_rules = [
|
| - (whitespace_re, TOKEN_WHITESPACE, None),
|
| - (float_re, TOKEN_FLOAT, None),
|
| - (integer_re, TOKEN_INTEGER, None),
|
| - (name_re, TOKEN_NAME, None),
|
| - (string_re, TOKEN_STRING, None),
|
| - (operator_re, TOKEN_OPERATOR, None)
|
| - ]
|
| -
|
| - # assemble the root lexing rule. because "|" is ungreedy
|
| - # we have to sort by length so that the lexer continues working
|
| - # as expected when we have parsing rules like <% for block and
|
| - # <%= for variables. (if someone wants asp like syntax)
|
| - # variables are just part of the rules if variable processing
|
| - # is required.
|
| - root_tag_rules = compile_rules(environment)
|
| -
|
| - # block suffix if trimming is enabled
|
| - block_suffix_re = environment.trim_blocks and '\\n?' or ''
|
| -
|
| - # strip leading spaces if lstrip_blocks is enabled
|
| - prefix_re = {}
|
| - if environment.lstrip_blocks:
|
| - # use '{%+' to manually disable lstrip_blocks behavior
|
| - no_lstrip_re = e('+')
|
| - # detect overlap between block and variable or comment strings
|
| - block_diff = c(r'^%s(.*)' % e(environment.block_start_string))
|
| - # make sure we don't mistake a block for a variable or a comment
|
| - m = block_diff.match(environment.comment_start_string)
|
| - no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
| - m = block_diff.match(environment.variable_start_string)
|
| - no_lstrip_re += m and r'|%s' % e(m.group(1)) or ''
|
| -
|
| - # detect overlap between comment and variable strings
|
| - comment_diff = c(r'^%s(.*)' % e(environment.comment_start_string))
|
| - m = comment_diff.match(environment.variable_start_string)
|
| - no_variable_re = m and r'(?!%s)' % e(m.group(1)) or ''
|
| -
|
| - lstrip_re = r'^[ \t]*'
|
| - block_prefix_re = r'%s%s(?!%s)|%s\+?' % (
|
| - lstrip_re,
|
| - e(environment.block_start_string),
|
| - no_lstrip_re,
|
| - e(environment.block_start_string),
|
| - )
|
| - comment_prefix_re = r'%s%s%s|%s\+?' % (
|
| - lstrip_re,
|
| - e(environment.comment_start_string),
|
| - no_variable_re,
|
| - e(environment.comment_start_string),
|
| - )
|
| - prefix_re['block'] = block_prefix_re
|
| - prefix_re['comment'] = comment_prefix_re
|
| - else:
|
| - block_prefix_re = '%s' % e(environment.block_start_string)
|
| -
|
| - self.newline_sequence = environment.newline_sequence
|
| - self.keep_trailing_newline = environment.keep_trailing_newline
|
| -
|
| - # global lexing rules
|
| - self.rules = {
|
| - 'root': [
|
| - # directives
|
| - (c('(.*?)(?:%s)' % '|'.join(
|
| - [r'(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*(?:\-%s\s*|%s))' % (
|
| - e(environment.block_start_string),
|
| - block_prefix_re,
|
| - e(environment.block_end_string),
|
| - e(environment.block_end_string)
|
| - )] + [
|
| - r'(?P<%s_begin>\s*%s\-|%s)' % (n, r, prefix_re.get(n,r))
|
| - for n, r in root_tag_rules
|
| - ])), (TOKEN_DATA, '#bygroup'), '#bygroup'),
|
| - # data
|
| - (c('.+'), TOKEN_DATA, None)
|
| - ],
|
| - # comments
|
| - TOKEN_COMMENT_BEGIN: [
|
| - (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
|
| - e(environment.comment_end_string),
|
| - e(environment.comment_end_string),
|
| - block_suffix_re
|
| - )), (TOKEN_COMMENT, TOKEN_COMMENT_END), '#pop'),
|
| - (c('(.)'), (Failure('Missing end of comment tag'),), None)
|
| - ],
|
| - # blocks
|
| - TOKEN_BLOCK_BEGIN: [
|
| - (c('(?:\-%s\s*|%s)%s' % (
|
| - e(environment.block_end_string),
|
| - e(environment.block_end_string),
|
| - block_suffix_re
|
| - )), TOKEN_BLOCK_END, '#pop'),
|
| - ] + tag_rules,
|
| - # variables
|
| - TOKEN_VARIABLE_BEGIN: [
|
| - (c('\-%s\s*|%s' % (
|
| - e(environment.variable_end_string),
|
| - e(environment.variable_end_string)
|
| - )), TOKEN_VARIABLE_END, '#pop')
|
| - ] + tag_rules,
|
| - # raw block
|
| - TOKEN_RAW_BEGIN: [
|
| - (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
|
| - e(environment.block_start_string),
|
| - block_prefix_re,
|
| - e(environment.block_end_string),
|
| - e(environment.block_end_string),
|
| - block_suffix_re
|
| - )), (TOKEN_DATA, TOKEN_RAW_END), '#pop'),
|
| - (c('(.)'), (Failure('Missing end of raw directive'),), None)
|
| - ],
|
| - # line statements
|
| - TOKEN_LINESTATEMENT_BEGIN: [
|
| - (c(r'\s*(\n|$)'), TOKEN_LINESTATEMENT_END, '#pop')
|
| - ] + tag_rules,
|
| - # line comments
|
| - TOKEN_LINECOMMENT_BEGIN: [
|
| - (c(r'(.*?)()(?=\n|$)'), (TOKEN_LINECOMMENT,
|
| - TOKEN_LINECOMMENT_END), '#pop')
|
| - ]
|
| - }
|
| -
|
| - def _normalize_newlines(self, value):
|
| - """Called for strings and template data to normalize it to unicode."""
|
| - return newline_re.sub(self.newline_sequence, value)
|
| -
|
| - def tokenize(self, source, name=None, filename=None, state=None):
|
| - """Calls tokeniter + tokenize and wraps it in a token stream.
|
| - """
|
| - stream = self.tokeniter(source, name, filename, state)
|
| - return TokenStream(self.wrap(stream, name, filename), name, filename)
|
| -
|
| - def wrap(self, stream, name=None, filename=None):
|
| - """This is called with the stream as returned by `tokenize` and wraps
|
| - every token in a :class:`Token` and converts the value.
|
| - """
|
| - for lineno, token, value in stream:
|
| - if token in ignored_tokens:
|
| - continue
|
| - elif token == 'linestatement_begin':
|
| - token = 'block_begin'
|
| - elif token == 'linestatement_end':
|
| - token = 'block_end'
|
| - # we are not interested in those tokens in the parser
|
| - elif token in ('raw_begin', 'raw_end'):
|
| - continue
|
| - elif token == 'data':
|
| - value = self._normalize_newlines(value)
|
| - elif token == 'keyword':
|
| - token = value
|
| - elif token == 'name':
|
| - value = str(value)
|
| - elif token == 'string':
|
| - # try to unescape string
|
| - try:
|
| - value = self._normalize_newlines(value[1:-1]) \
|
| - .encode('ascii', 'backslashreplace') \
|
| - .decode('unicode-escape')
|
| - except Exception as e:
|
| - msg = str(e).split(':')[-1].strip()
|
| - raise TemplateSyntaxError(msg, lineno, name, filename)
|
| - # if we can express it as bytestring (ascii only)
|
| - # we do that for support of semi broken APIs
|
| - # as datetime.datetime.strftime. On python 3 this
|
| - # call becomes a noop thanks to 2to3
|
| - try:
|
| - value = str(value)
|
| - except UnicodeError:
|
| - pass
|
| - elif token == 'integer':
|
| - value = int(value)
|
| - elif token == 'float':
|
| - value = float(value)
|
| - elif token == 'operator':
|
| - token = operators[value]
|
| - yield Token(lineno, token, value)
|
| -
|
| - def tokeniter(self, source, name, filename=None, state=None):
|
| - """This method tokenizes the text and returns the tokens in a
|
| - generator. Use this method if you just want to tokenize a template.
|
| - """
|
| - source = text_type(source)
|
| - lines = source.splitlines()
|
| - if self.keep_trailing_newline and source:
|
| - for newline in ('\r\n', '\r', '\n'):
|
| - if source.endswith(newline):
|
| - lines.append('')
|
| - break
|
| - source = '\n'.join(lines)
|
| - pos = 0
|
| - lineno = 1
|
| - stack = ['root']
|
| - if state is not None and state != 'root':
|
| - assert state in ('variable', 'block'), 'invalid state'
|
| - stack.append(state + '_begin')
|
| - else:
|
| - state = 'root'
|
| - statetokens = self.rules[stack[-1]]
|
| - source_length = len(source)
|
| -
|
| - balancing_stack = []
|
| -
|
| - while 1:
|
| - # tokenizer loop
|
| - for regex, tokens, new_state in statetokens:
|
| - m = regex.match(source, pos)
|
| - # if no match we try again with the next rule
|
| - if m is None:
|
| - continue
|
| -
|
| - # we only match blocks and variables if braces / parentheses
|
| - # are balanced. continue parsing with the lower rule which
|
| - # is the operator rule. do this only if the end tags look
|
| - # like operators
|
| - if balancing_stack and \
|
| - tokens in ('variable_end', 'block_end',
|
| - 'linestatement_end'):
|
| - continue
|
| -
|
| - # tuples support more options
|
| - if isinstance(tokens, tuple):
|
| - for idx, token in enumerate(tokens):
|
| - # failure group
|
| - if token.__class__ is Failure:
|
| - raise token(lineno, filename)
|
| - # bygroup is a bit more complex, in that case we
|
| - # yield for the current token the first named
|
| - # group that matched
|
| - elif token == '#bygroup':
|
| - for key, value in iteritems(m.groupdict()):
|
| - if value is not None:
|
| - yield lineno, key, value
|
| - lineno += value.count('\n')
|
| - break
|
| - else:
|
| - raise RuntimeError('%r wanted to resolve '
|
| - 'the token dynamically'
|
| - ' but no group matched'
|
| - % regex)
|
| - # normal group
|
| - else:
|
| - data = m.group(idx + 1)
|
| - if data or token not in ignore_if_empty:
|
| - yield lineno, token, data
|
| - lineno += data.count('\n')
|
| -
|
| - # strings as token just are yielded as it.
|
| - else:
|
| - data = m.group()
|
| - # update brace/parentheses balance
|
| - if tokens == 'operator':
|
| - if data == '{':
|
| - balancing_stack.append('}')
|
| - elif data == '(':
|
| - balancing_stack.append(')')
|
| - elif data == '[':
|
| - balancing_stack.append(']')
|
| - elif data in ('}', ')', ']'):
|
| - if not balancing_stack:
|
| - raise TemplateSyntaxError('unexpected \'%s\'' %
|
| - data, lineno, name,
|
| - filename)
|
| - expected_op = balancing_stack.pop()
|
| - if expected_op != data:
|
| - raise TemplateSyntaxError('unexpected \'%s\', '
|
| - 'expected \'%s\'' %
|
| - (data, expected_op),
|
| - lineno, name,
|
| - filename)
|
| - # yield items
|
| - if data or tokens not in ignore_if_empty:
|
| - yield lineno, tokens, data
|
| - lineno += data.count('\n')
|
| -
|
| - # fetch new position into new variable so that we can check
|
| - # if there is a internal parsing error which would result
|
| - # in an infinite loop
|
| - pos2 = m.end()
|
| -
|
| - # handle state changes
|
| - if new_state is not None:
|
| - # remove the uppermost state
|
| - if new_state == '#pop':
|
| - stack.pop()
|
| - # resolve the new state by group checking
|
| - elif new_state == '#bygroup':
|
| - for key, value in iteritems(m.groupdict()):
|
| - if value is not None:
|
| - stack.append(key)
|
| - break
|
| - else:
|
| - raise RuntimeError('%r wanted to resolve the '
|
| - 'new state dynamically but'
|
| - ' no group matched' %
|
| - regex)
|
| - # direct state name given
|
| - else:
|
| - stack.append(new_state)
|
| - statetokens = self.rules[stack[-1]]
|
| - # we are still at the same position and no stack change.
|
| - # this means a loop without break condition, avoid that and
|
| - # raise error
|
| - elif pos2 == pos:
|
| - raise RuntimeError('%r yielded empty string without '
|
| - 'stack change' % regex)
|
| - # publish new function and start again
|
| - pos = pos2
|
| - break
|
| - # if loop terminated without break we haven't found a single match
|
| - # either we are at the end of the file or we have a problem
|
| - else:
|
| - # end of text
|
| - if pos >= source_length:
|
| - return
|
| - # something went wrong
|
| - raise TemplateSyntaxError('unexpected char %r at %d' %
|
| - (source[pos], pos), lineno,
|
| - name, filename)
|
|
|