fixed some NameErrors
[jinja2.git] / jinja2 / datastructure.py
1 # -*- coding: utf-8 -*-
2 """
3     jinja2.datastructure
4     ~~~~~~~~~~~~~~~~~~~~
5
6     Module that helds several data types used in the template engine.
7
8     :copyright: 2007 by Armin Ronacher.
9     :license: BSD, see LICENSE for more details.
10 """
11 from operator import itemgetter
12 from collections import deque
13 from jinja2.exceptions import TemplateSyntaxError, TemplateRuntimeError
14
15
16 class Token(tuple):
17     """
18     Token class.
19     """
20     __slots__ = ()
21     lineno, type, value = (property(itemgetter(x)) for x in range(3))
22
23     def __new__(cls, lineno, type, value):
24         return tuple.__new__(cls, (lineno, intern(str(type)), value))
25
26     def __str__(self):
27         from jinja.lexer import keywords, reverse_operators
28         if self.type in keywords:
29             return self.type
30         elif self.type in reverse_operators:
31             return reverse_operators[self.type]
32         return '%s:%s' % (self.type, self.value)
33
34     def test(self, expr):
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
37         string values!
38         """
39         # here we do a regular string equality check as test_many is usually
40         # passed an iterable of not interned strings.
41         if self.type == expr:
42             return True
43         elif ':' in expr:
44             return expr.split(':', 1) == [self.type, self.value]
45         return False
46
47     def test_many(self, iterable):
48         """Test against multiple token expressions."""
49         for expr in iterable:
50             if self.test(expr):
51                 return True
52         return False
53
54     def __repr__(self):
55         return 'Token(%r, %r, %r)' % (
56             self.lineno,
57             self.type,
58             self.value
59         )
60
61
62 class TokenStreamIterator(object):
63     """
64     The iterator for tokenstreams.  Iterate over the stream
65     until the eof token is reached.
66     """
67
68     def __init__(self, stream):
69         self._stream = stream
70
71     def __iter__(self):
72         return self
73
74     def next(self):
75         token = self._stream.current
76         if token.type == 'eof':
77             self._stream.close()
78             raise StopIteration()
79         self._stream.next(False)
80         return token
81
82
83 class TokenStream(object):
84     """
85     A token stream wraps a generator and supports pushing tokens back.
86     It also provides some functions to expect tokens and similar stuff.
87
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!
92     """
93
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
99         self.next()
100
101     def __iter__(self):
102         return TokenStreamIterator(self)
103
104     def __nonzero__(self):
105         """Are we at the end of the tokenstream?"""
106         return bool(self._pushed) or self.current.type != 'eof'
107
108     eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
109
110     def push(self, token):
111         """Push a token back to the stream."""
112         self._pushed.append(token)
113
114     def look(self):
115         """Look at the next token."""
116         old_token = self.next()
117         result = self.current
118         self.push(result)
119         self.current = old_token
120         return result
121
122     def skip(self, n):
123         """Got n tokens ahead."""
124         for x in xrange(n):
125             self.next()
126
127     def next(self, skip_eol=True):
128         """Go one token ahead and return the old one"""
129         rv = self.current
130         while 1:
131             if self._pushed:
132                 self.current = self._pushed.popleft()
133             elif self.current.type is not 'eof':
134                 try:
135                     self.current = self._next()
136                 except StopIteration:
137                     self.close()
138             if not skip_eol or self.current.type is not 'eol':
139                 break
140         return rv
141
142     def close(self):
143         """Close the stream."""
144         self.current = Token(self.current.lineno, 'eof', '')
145         self._next = None
146
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),
152                                       self.current.lineno,
153                                       self.filename)
154         try:
155             return self.current
156         finally:
157             self.next()