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
21 lineno, type, value = (property(itemgetter(x)) for x in range(3))
23 def __new__(cls, lineno, type, value):
24 return tuple.__new__(cls, (lineno, intern(str(type)), value))
27 from jinja.lexer import keywords, reverse_operators
28 if self.type in keywords:
30 elif self.type in reverse_operators:
31 return reverse_operators[self.type]
32 return '%s:%s' % (self.type, self.value)
35 """Test a token against a token expression. This can either be a
36 token type or 'token_type:token_value'. This can only test against
39 # here we do a regular string equality check as test_many is usually
40 # passed an iterable of not interned strings.
44 return expr.split(':', 1) == [self.type, self.value]
47 def test_many(self, iterable):
48 """Test against multiple token expressions."""
55 return 'Token(%r, %r, %r)' % (
62 class TokenStreamIterator(object):
64 The iterator for tokenstreams. Iterate over the stream
65 until the eof token is reached.
68 def __init__(self, stream):
75 token = self._stream.current
76 if token.type == 'eof':
79 self._stream.next(False)
83 class TokenStream(object):
85 A token stream wraps a generator and supports pushing tokens back.
86 It also provides some functions to expect tokens and similar stuff.
88 Important note: Do never push more than one token back to the
89 stream. Although the stream object won't stop you
90 from doing so, the behavior is undefined. Multiple
91 pushed tokens are only used internally!
94 def __init__(self, generator, filename):
95 self._next = generator.next
96 self._pushed = deque()
97 self.current = Token(1, 'initial', '')
98 self.filename = filename
102 return TokenStreamIterator(self)
104 def __nonzero__(self):
105 """Are we at the end of the tokenstream?"""
106 return bool(self._pushed) or self.current.type != 'eof'
108 eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
110 def push(self, token):
111 """Push a token back to the stream."""
112 self._pushed.append(token)
115 """Look at the next token."""
116 old_token = self.next()
117 result = self.current
119 self.current = old_token
123 """Got n tokens ahead."""
127 def next(self, skip_eol=True):
128 """Go one token ahead and return the old one"""
132 self.current = self._pushed.popleft()
133 elif self.current.type is not 'eof':
135 self.current = self._next()
136 except StopIteration:
138 if not skip_eol or self.current.type is not 'eol':
143 """Close the stream."""
144 self.current = Token(self.current.lineno, 'eof', '')
147 def expect(self, expr):
148 """Expect a given token type and return it"""
149 if not self.current.test(expr):
150 raise TemplateSyntaxError("expected token %r, got %r" %
151 (expr, self.current),