1 # -*- coding: utf-8 -*-
6 Module that helds several data types used in the template engine.
8 :copyright: 2007 by Armin Ronacher.
9 :license: BSD, see LICENSE for more details.
11 from operator import itemgetter
12 from collections import deque
13 from jinja2.exceptions import TemplateSyntaxError, TemplateRuntimeError
24 lineno, type, value = (property(itemgetter(x)) for x in range(3))
26 def __new__(cls, lineno, type, value):
27 return tuple.__new__(cls, (lineno, intern(str(type)), value))
30 from jinja.lexer import keywords, reverse_operators
31 if self.type in keywords:
33 elif self.type in reverse_operators:
34 return reverse_operators[self.type]
38 return 'Token(%r, %r, %r)' % (
45 class TokenStreamIterator(object):
47 The iterator for tokenstreams. Iterate over the stream
48 until the eof token is reached.
51 def __init__(self, stream):
58 token = self._stream.current
59 if token.type == 'eof':
62 self._stream.next(False)
66 class TokenStream(object):
68 A token stream wraps a generator and supports pushing tokens back.
69 It also provides some functions to expect tokens and similar stuff.
71 Important note: Do never push more than one token back to the
72 stream. Although the stream object won't stop you
73 from doing so, the behavior is undefined. Multiple
74 pushed tokens are only used internally!
77 def __init__(self, generator, filename):
78 self._next = generator.next
79 self._pushed = deque()
80 self.current = Token(1, 'initial', '')
81 self.filename = filename
85 return TokenStreamIterator(self)
87 def __nonzero__(self):
88 """Are we at the end of the tokenstream?"""
89 return bool(self._pushed) or self.current.type != 'eof'
91 eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
93 def push(self, token):
94 """Push a token back to the stream."""
95 self._pushed.append(token)
98 """Look at the next token."""
99 old_token = self.current
106 """Got n tokens ahead."""
110 def next(self, skip_eol=True):
111 """Go one token ahead and return the old one"""
115 self.current = self._pushed.popleft()
116 elif self.current.type is not 'eof':
118 self.current = self._next()
119 except StopIteration:
121 if not skip_eol or self.current.type is not 'eol':
126 """Close the stream."""
127 self.current = Token(self.current.lineno, 'eof', '')
130 def expect(self, token_type, token_value=_missing):
131 """Expect a given token type and return it"""
132 if self.current.type is not token_type:
133 raise TemplateSyntaxError("expected token %r, got %r" %
134 (token_type, self.current.type),
137 elif token_value is not _missing and \
138 self.current.value != token_value:
139 raise TemplateSyntaxError("expected %r, got %r" %
140 (token_value, self.current.value),