root/livinglogic.python.xist/src/ll/ul4c.py @ 3785:3aa5a5d89287

Revision 3785:3aa5a5d89287, 87.4 KB (checked in by Walter Doerwald <walter@…>, 10 years ago)

Fix UL4 templates that produce no output.

As the generated Python sourcecode didn't contain any yield statements, the
resulting function was an ordinary function instead of a generator.

Line 
1# -*- coding: utf-8 -*-
2
3## Copyright 2009 by LivingLogic AG, Bayreuth/Germany
4## Copyright 2009 by Walter Dörwald
5##
6## All Rights Reserved
7##
8## See ll/__init__.py for the license
9
10
11"""
12:mod:`ll.ul4c` provides templating for XML/HTML as well as any other text-based
13format. A template defines placeholders for data output and basic logic (like
14loops and conditional blocks), that define how the final rendered output will
15look.
16
17:mod:`ll.ul4c` compiles a template to a bytecode format, which makes it possible
18to implement template renderers in multiple programming languages.
19"""
20
21from __future__ import division
22
23__docformat__ = "reStructuredText"
24
25
26import re, datetime, StringIO, locale
27
28from ll import spark, color
29
30
31# Regular expression used for splitting dates
32datesplitter = re.compile("[-T:.]")
33
34
35###
36### Location information
37###
38
39class Location(object):
40    """
41    A :class:`Location` object contains information about the location of a
42    template tag.
43    """
44    __slots__ = ("source", "type", "starttag", "endtag", "startcode", "endcode")
45
46    def __init__(self, source, type, starttag, endtag, startcode, endcode):
47        """
48        Create a new :class:`Location` object. The arguments have the following
49        meaning:
50
51        :var:`source`
52            The complete source string
53
54        :var:`type`
55            The tag type (i.e. ``"for"``, ``"if"``, etc.)
56
57        :var:`starttag`
58            The start position of the start delimiter.
59
60        :var:`endtag`
61            The end position of the end delimiter.
62
63        :var:`startcode`
64            The start position of the tag code.
65
66        :var:`endcode`
67            The end position of the tag code.
68        """
69        self.source = source
70        self.type = type
71        self.starttag = starttag
72        self.endtag = endtag
73        self.startcode = startcode
74        self.endcode = endcode
75
76    @property
77    def code(self):
78        return self.source[self.startcode:self.endcode]
79
80    @property
81    def tag(self):
82        return self.source[self.starttag:self.endtag]
83
84    def __repr__(self):
85        return "<%s.%s %s at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self, id(self))
86
87    def pos(self):
88        lastlinefeed = self.source.rfind("\n", 0, self.starttag)
89        if lastlinefeed >= 0:
90            return (self.source.count("\n", 0, self.starttag)+1, self.starttag-lastlinefeed)
91        else:
92            return (1, self.starttag + 1)
93
94    def __str__(self):
95        (line, col) = self.pos()
96        return "%r at %d (line %d, col %d)" % (self.tag, self.starttag+1, line, col)
97
98
99###
100### Exceptions
101###
102
103class Error(Exception):
104    """
105    Exception class that wraps another exception and provides a location.
106    """
107    def __init__(self, location, cause):
108        self.location = location
109        self.cause = cause
110
111    def __repr__(self):
112        return "<%s.%s in %s at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, self.location, id(self))
113
114    def __str__(self):
115        path = []
116
117        exc = self
118        while isinstance(exc, Error):
119            if not path or path[-1] is not exc.location:
120                path.append(exc.location)
121            exc = exc.cause
122        name = exc.__class__.__name__
123        module = exc.__class__.__module__
124        if module != "exceptions":
125            name = "%s.%s" % (module, name)
126        return "%s %s %s" % (name, "".join("in %s:" % location for location in path), exc)
127
128
129class LexicalError(Exception):
130    def __init__(self, start, end, input):
131        self.start = start
132        self.end = end
133        self.input = input
134
135    def __str__(self):
136        return "Unmatched input %r" % self.input
137
138
139class SyntaxError(Exception):
140    def __init__(self, token):
141        self.token = token
142
143    def __str__(self):
144        return "Lexical error near %r" % str(self.token)
145
146
147class UnterminatedStringError(Exception):
148    """
149    Exception that is raised by the parser when a string constant is not
150    terminated.
151    """
152    def __str__(self):
153        return "Unterminated string"
154
155
156class BlockError(Exception):
157    """
158    Exception that is raised by the compiler when an illegal block structure is
159    detected (e.g. an ``endif`` without a previous ``if``).
160    """
161
162    def __init__(self, message):
163        self.message = message
164
165    def __str__(self):
166        return self.message
167
168
169class UnknownFunctionError(Exception):
170    """
171    Exception that is raised by the renderer if the function to be executed by
172    the ``callfunc0``, ``callfunc1``, ``callfunc2``, ``callfunc3`` or
173    ``callfunc4`` opcodes is unknown.
174    """
175
176    def __init__(self, funcname):
177        self.funcname = funcname
178
179    def __str__(self):
180        return "function %r unknown" % self.funcname
181
182
183class UnknownMethodError(Exception):
184    """
185    Exception that is raised by the renderer if the method to be executed by the
186    ``callmeth0``, ``callmeth1``, ``callmeth2``  or ``callmeth3`` opcodes is
187    unknown.
188    """
189
190    def __init__(self, methname):
191        self.methname = methname
192
193    def __str__(self):
194        return "method %r unknown" % self.methname
195
196
197class UnknownOpcodeError(Exception):
198    """
199    Exception that is raised when an unknown opcode is encountered by the renderer.
200    """
201
202    def __init__(self, opcode):
203        self.opcode = opcode
204
205    def __str__(self):
206        return "opcode %r unknown" % self.opcode
207
208
209class OutOfRegistersError(Exception):
210    """
211    Exception that is raised by the compiler when there are no more free
212    registers. This might happen with complex expressions in tag code.
213    """
214
215    def __str__(self):
216        return "out of registers"
217
218
219###
220### opcode class
221###
222
223class Opcode(object):
224    """
225    An :class:`Opcode` stores an opcode. An :class:`Opcode` object has the
226    following attributes:
227
228    :attr:`code` : string or :const:`None`
229        The opcode type (see below for a list).
230
231    :attr:`r1`, :attr:`r2`, :attr:`r3`, :attr:`r4`, :attr:`r5` : integer or :const:`None`
232         Register specifications (for the sources or the target of the opcode)
233
234    :attr:`arg` : string or :const:`None`
235        Used if the opcode requires an additional argument (like a variable name
236        or the value of a constant).
237
238    :attr:`location` : :class:`Location` object
239        The location of the tag to which this opcode belongs.
240
241    The following opcode types are available:
242
243    :const:`None`:
244        Print text. The text is available from ``location.code``.
245
246    ``"print"``:
247        Print the content of register :attr:`r1`. (If the object in the register
248        is not a string, it will be converted to a string first.)
249
250    ``"loadnone"``:
251        Load the constant :const:`None` into register :attr:`r1`.
252
253    ``"loadfalse"``:
254        Load the constant :const:`False` into register :attr:`r1`.
255
256    ``"loadtrue"``:
257        Load the constant :const:`True` into register :attr:`r1`.
258
259    ``"loadstr"``:
260        Load the string :attr:`arg` into register :attr:`r1`.
261
262    ``"loadint"``:
263        Load the integer value :attr:`arg` into register :attr:`r1`.
264
265    ``"loadfloat"``:
266        Load the float value :attr:`arg` into register :attr:`r1`.
267
268    ``"loaddate"``:
269        Load the date value :attr:`arg` into register :attr:`r1`. :attr:`arg` must
270        be in ISO format (e.g. ``2008-07-02T11:05:55.460464``).
271
272    ``"loadcolor"``:
273        Load the color value :attr:`arg` into register :attr:`r1`. :attr:`arg` must
274        be in the format ``rrggbbaa``).
275
276    ``"buildlist"``:
277        Load an empty list into register :attr:`r1`.
278
279    ``"builddict"``:
280        Load an empty dictionary into register :attr:`r1`.
281
282    ``"addlist"``
283        Append the object in register :attr:`r2` to the list in register :attr:`r1`.
284   
285    ``"adddict"``
286        Add a new entry to the dictionary in register :attr:`r1`. The object in
287        :attr:`r2` is the key and the object in register :attr:`r3` is the value.
288   
289    ``"updatedict"``
290        Update the dictionary in register :attr:`r1` with the items from the
291        dictionary in :attr:`r2`.
292   
293    ``"loadvar"``:
294        Load the variable named :attr:`arg` into the register :attr:`r1`.
295
296    ``"storevar"``:
297        Store the content of register :attr:`r1` in the variable named :attr:`arg`.
298
299    ``"addvar"``:
300        Add the content of register :attr:`r1` to the variable named :attr:`arg`.
301
302    ``"for"``:
303        Start a loop over the object in the register :attr:`r2` and store the
304        object from each loop iteration in the register :attr:`r1`.
305
306    ``"endfor"``:
307        End the innermost running ``for`` loop.
308
309    ``"break"``:
310        Breaks the innermost running ``for`` loop.
311
312    ``"continue"``:
313        Continues the innermost running ``for`` loop (i.e. jumps back to the
314        start of the loop body).
315
316    ``"if"``:
317        Start a conditional block. If the objects in the register :attr:`r1` is
318        true the block will be executed. The "block" consists of all opcodes after
319        the ``if`` upto the matching ``else`` or ``endif`` opcode.
320
321    ``"else"``:
322        Start the else branch of the previous ``if``.
323
324    ``"endif"``:
325        End a conditional block.
326
327    ``"getattr"``:
328        Get the attribute named :attr:`arg` from the object in register :attr:`r2`
329        and store it in register :attr:`r1`.
330
331    ``"getitem"``:
332        Get an item from the object in register :attr:`r2`. If this object is a
333        list or string the object in register :attr:`r3` will be used as the
334        index. If it is a dictionary :attr:`r3` will be used as the key. The
335        result will be stored in register :attr:`r1`.
336
337    ``"getslice12"``:
338        Get an slice from the object in register :attr:`r2`. The object in
339        register :attr:`r3` (which must be an ``int`` or :const:`None`) specifies
340        the start index, the object in register :attr:`r4` specifies the end index.
341        The result will be stored in register :attr:`r1`.
342
343    ``"getslice1"``:
344        Similar to ``getslice12`` except that the end index is always the length
345        of the object.
346
347    ``"getslice2"``:
348        Similar to ``getslice12`` except that the start index is always 0 and the
349        end index is in register :attr:`r3`.
350
351    ``"not"``:
352        Invert the truth value of the object in register :attr:`r2` and stores the
353        resulting bool in the register :attr:`r1`.
354
355    ``"eq"``:
356        Compare the objects in register :attr:`r2` and :attr:`r3` and store
357        ``True`` in the register :attr:`r1` if they are equal, ``False`` otherwise.
358
359    ``"ne"``:
360        Compare the objects in register :attr:`r2` and :attr:`r3` and store
361        ``False`` in the register :attr:`r1` if they are equal, ``True`` otherwise.
362
363    ``"lt"``:
364        Does a "<" comparison of the objects in register :attr:`r2` and :attr:`r3`
365        and stores the result in register :attr:`r1`.
366
367    ``"le"``:
368        Does a "<=" comparison of the objects in register :attr:`r2` and :attr:`r3`
369        and stores the result in register :attr:`r1`.
370
371    ``"gt"``:
372        Does a ">" comparison of the objects in register :attr:`r2` and :attr:`r3`
373        and stores the result in register :attr:`r1`.
374
375    ``"ge"``:
376        Does a ">=" comparison of the objects in register :attr:`r2` and :attr:`r3`
377        and stores the result in register :attr:`r1`.
378
379    ``"contains"``:
380        Test whether the object in register :attr:`r3` contains the object in
381        register :attr:`r2` (either as a key if :attr:`r3` is a dictionary or as
382        an item if it's a list or as a substring if it's a string) and store
383        ``True`` into the register :attr:`r1` if it does, ``False`` otherwise.
384
385    ``"notcontains"``:
386        Test whether the object in register :attr:`r3` contains the object in
387        register :attr:`r2` (either as a key if :attr:`r3` is a dictionary or as
388        an item if it's a list or as a substring if it's a string) and store
389        ``False`` into the register :attr:`r1` if it does, ``True`` otherwise.
390
391    ``"or"``:
392        Check the truth value of the two objects in registers :attr:`r2` and
393        :attr:`r3` and store :attr:`r2` in the register :attr:`r1` if it is true,
394        :attr:`r3` otherwise).
395
396    ``"and"``:
397        Check the truth value of the two objects in registers :attr:`r2` and
398        :attr:`r3` and store :attr:`r3` in the register :attr:`r1` if :attr:`r2`
399        is true, :attr:`r3` otherwise).
400
401    ``"mod"``:
402        Does a modulo operation: Calculates :attr:`r2` modulo :attr:`r3` and stores
403        the result in register :attr:`r1`.
404
405    ``"callfunc0"``:
406        Call the function named :attr:`arg` without any arguments and store the
407        return value in register :attr:`r1`.
408
409    ``"callfunc1"``:
410        Call the function named :attr:`arg` with the content of register :attr:`r2`
411        as an argument and store the return value in register :attr:`r1`.
412
413    ``"callfunc2"``:
414        Call the function named :attr:`arg` with the contents of register
415        :attr:`r2` and :attr:`r3` as the two arguments and store the return value
416        in register :attr:`r1`.
417
418    ``"callfunc3"``:
419        Call the function named :attr:`arg` with the contents of register
420        :attr:`r2`, :attr:`r3` and :attr:`r4` as the three arguments and store
421        the return value in register :attr:`r1`.
422
423    ``"callfunc4"``:
424        Call the function named :attr:`arg` with the contents of register
425        :attr:`r2`, :attr:`r3`, :attr:`r4` and :attr:`r5` as the four arguments
426        and store the return value in register :attr:`r1`.
427
428    ``"callmeth0"``:
429        Call the method named :attr:`arg` on the object in register :attr:`r2`
430        and store the return value in register :attr:`r1`.
431
432    ``"callmeth1"``:
433        Call the method named :attr:`arg` on the object in register :attr:`r2`
434        using the object in register :attr:`r3` as the only argument and store the
435        return value in register :attr:`r1`.
436
437    ``"callmeth2"``:
438        Call the method named :attr:`arg` on the object in register :attr:`r2`
439        using the objects in register :attr:`r3` and :attr:`r4` as arguments and
440        store the return value in register :attr:`r1`.
441
442    ``"callmeth3"``:
443        Call the method named :attr:`arg` on the object in register :attr:`r2`
444        using the objects in register :attr:`r3`, :attr:`r4` and :attr:`r5` as
445        arguments and store the return value in register :attr:`r1`.
446
447    ``"render"``:
448        Render the template in the attribute :attr:`r1`. The content of register
449        :attr:`r2` (which must be a dictionary) will be passed to the template as
450        the variable dictionary.
451    """
452    __slots__ = ("code", "r1", "r2", "r3", "r4", "r5", "arg", "location", "jump")
453
454    def __init__(self, code, r1=None, r2=None, r3=None, r4=None, r5=None, arg=None, location=None):
455        self.code = code
456        self.r1 = r1
457        self.r2 = r2
458        self.r3 = r3
459        self.r4 = r4
460        self.r5 = r5
461        self.arg = arg
462        self.location = location
463        self.jump = None
464
465    def __repr__(self):
466        v = ["<", self.__class__.__name__, " code=%r" % self.code]
467        for attrname in ("r1", "r2", "r3", "r4", "r5", "arg"):
468            attr = getattr(self, attrname)
469            if attr is not None:
470                v.append(" %s=%r" % (attrname, attr))
471        if self.code is None:
472            v.append(" text=%r" % self.location.code)
473        v.append(" at 0x%x>" % id(self))
474        return "".join(v)
475
476    def __str__(self):
477        if self.code is None:
478            return "print %r" % self.location.code
479        elif self.code == "print":
480            return "print r%r" % self.r1
481        elif self.code == "printx":
482            return "print xmlescape(r%r)" % self.r1
483        elif self.code == "loadnone":
484            return "r%r = None" % self.r1
485        elif self.code == "loadfalse":
486            return "r%r = False" % self.r1
487        elif self.code == "loadtrue":
488            return "r%r = True" % self.r1
489        elif self.code == "loadstr":
490            return "r%r = %r" % (self.r1, self.arg)
491        elif self.code == "loadint":
492            return "r%r = %s" % (self.r1, self.arg)
493        elif self.code == "loadfloat":
494            return "r%r = %s" % (self.r1, self.arg)
495        elif self.code == "loaddate":
496            return "r%r = %s" % (self.r1, self.arg)
497        elif self.code == "loadcolor":
498            return "r%r = #%s" % (self.r1, self.arg)
499        elif self.code == "buildlist":
500            return "r%r = []" % (self.r1)
501        elif self.code == "builddict":
502            return "r%r = {}" % (self.r1)
503        elif self.code == "addlist":
504            return "r%r.append(r%r)" % (self.r1, self.r2)
505        elif self.code == "adddict":
506            return "r%r[r%r] = r%r" % (self.r1, self.r2, self.r3)
507        elif self.code == "updatedict":
508            return "r%r.update(r%r)" % (self.r1, self.r2)
509        elif self.code == "loadvar":
510            return "r%r = vars[%r]" % (self.r1, self.arg)
511        elif self.code == "storevar":
512            return "vars[%r] = r%r" % (self.arg, self.r1)
513        elif self.code == "addvar":
514            return "vars[%r] += r%r" % (self.arg, self.r1)
515        elif self.code == "subvar":
516            return "vars[%r] -= r%r" % (self.arg, self.r1)
517        elif self.code == "mulvar":
518            return "vars[%r] *= r%r" % (self.arg, self.r1)
519        elif self.code == "truedivvar":
520            return "vars[%r] /= r%r" % (self.arg, self.r1)
521        elif self.code == "floordivvar":
522            return "vars[%r] //= r%r" % (self.arg, self.r1)
523        elif self.code == "modvar":
524            return "vars[%r] %%= r%r" % (self.arg, self.r1)
525        elif self.code == "delvar":
526            return "del vars[%r]" % self.arg
527        elif self.code == "for":
528            return "for r%r in r%r" % (self.r1, self.r2)
529        elif self.code == "endfor":
530            return "endfor"
531        elif self.code == "break":
532            return "break"
533        elif self.code == "continue":
534            return "continue"
535        elif self.code == "if":
536            return "if r%r" % self.r1
537        elif self.code == "else":
538            return "else"
539        elif self.code == "endif":
540            return "endif"
541        elif self.code == "getattr":
542            return "r%r = getattr(r%r, %r)" % (self.r1, self.r2, self.arg)
543        elif self.code == "getitem":
544            return "r%r = r%r[r%r]" % (self.r1, self.r2, self.r3)
545        elif self.code == "getslice1":
546            return "r%r = r%r[r%r:]" % (self.r1, self.r2, self.r3)
547        elif self.code == "getslice2":
548            return "r%r = r%r[:r%r]" % (self.r1, self.r2, self.r3)
549        elif self.code == "getslice12":
550            return "r%r = r%r[r%r:r%r]" % (self.r1, self.r2, self.r3, self.r4)
551        elif self.code == "not":
552            return "r%r = not r%r" % (self.r1, self.r2)
553        elif self.code == "eq":
554            return "r%r = r%r == r%r" % (self.r1, self.r2, self.r3)
555        elif self.code == "ne":
556            return "r%r = r%r != r%r" % (self.r1, self.r2, self.r3)
557        elif self.code == "lt":
558            return "r%r = r%r < r%r" % (self.r1, self.r2, self.r3)
559        elif self.code == "le":
560            return "r%r = r%r <= r%r" % (self.r1, self.r2, self.r3)
561        elif self.code == "gt":
562            return "r%r = r%r > r%r" % (self.r1, self.r2, self.r3)
563        elif self.code == "ge":
564            return "r%r = r%r >= r%r" % (self.r1, self.r2, self.r3)
565        elif self.code == "contains":
566            return "r%r = r%r in r%r" % (self.r1, self.r2, self.r3)
567        elif self.code == "notcontains":
568            return "r%r = r%r not in r%r" % (self.r1, self.r2, self.r3)
569        elif self.code == "add":
570            return "r%r = r%r + r%r" % (self.r1, self.r2, self.r3)
571        elif self.code == "sub":
572            return "r%r = r%r - r%r" % (self.r1, self.r2, self.r3)
573        elif self.code == "mul":
574            return "r%r = r%r * r%r" % (self.r1, self.r2, self.r3)
575        elif self.code == "floordiv":
576            return "r%r = r%r // r%r" % (self.r1, self.r2, self.r3)
577        elif self.code == "truediv":
578            return "r%r = r%r / r%r" % (self.r1, self.r2, self.r3)
579        elif self.code == "and":
580            return "r%r = r%r and r%r" % (self.r1, self.r2, self.r3)
581        elif self.code == "or":
582            return "r%r = r%r or r%r" % (self.r1, self.r2, self.r3)
583        elif self.code == "mod":
584            return "r%r = r%r %% r%r" % (self.r1, self.r2, self.r3)
585        elif self.code == "neg":
586            return "r%r = -r%r" % (self.r1, self.r2)
587        elif self.code == "callfunc0":
588            return "r%r = %s()" % (self.r1, self.arg)
589        elif self.code == "callfunc1":
590            return "r%r = %s(r%r)" % (self.r1, self.arg, self.r2)
591        elif self.code == "callfunc2":
592            return "r%r = %s(r%r, r%r)" % (self.r1, self.arg, self.r2, self.r3)
593        elif self.code == "callfunc3":
594            return "r%r = %s(r%r, r%r, r%r)" % (self.r1, self.arg, self.r2, self.r3, self.r4)
595        elif self.code == "callfunc4":
596            return "r%r = %s(r%r, r%r, r%r, r%r)" % (self.r1, self.arg, self.r2, self.r3, self.r4, self.r5)
597        elif self.code == "callmeth0":
598            return "r%r = r%r.%s()" % (self.r1, self.r2, self.arg)
599        elif self.code == "callmeth1":
600            return "r%r = r%r.%s(r%r)" % (self.r1, self.r2, self.arg, self.r3)
601        elif self.code == "callmeth2":
602            return "r%r = r%r.%s(r%r, r%r)" % (self.r1, self.r2, self.arg, self.r3, self.r4)
603        elif self.code == "callmeth3":
604            return "r%r = r%r.%s(r%r, r%r, r%r)" % (self.r1, self.r2, self.arg, self.r3, self.r4, self.r5)
605        elif self.code == "callmethkw":
606            return "r%r = r%r.%s(**r%r)" % (self.r1, self.r2, self.arg, self.r3)
607        elif self.code == "render":
608            return "render r%r(r%r)" % (self.r1, self.r2)
609        else:
610            raise UnknownOpcodeError(self.code)
611
612
613class Template(object):
614    """
615    A template object can be compiled via the class method :meth:`compile` from
616    source. It can be loaded from the compiled format via :meth:`load` (from a
617    stream) or :meth:`loads` (from a string).
618   
619    The compiled format can be generated with the methods :meth:`dump` (which
620    dumps the format to a stream) or :meth:`dumps` (which returns a string with
621    the compiled format).
622
623    Rendering the template can be done with the methods :meth:`render` (which
624    is a generator) or :meth:`renders` (which returns a string).
625    """
626    version = "10"
627
628    def __init__(self):
629        self.startdelim = None
630        self.enddelim = None
631        self.source = None
632        self.opcodes = None
633        # The following is used for converting the opcodes back to executable Python code
634        self._pythonfunction = None
635
636    @classmethod
637    def loads(cls, data):
638        """
639        The class method :meth:`loads` loads the template from string :var:`data`.
640        :var:`data` must contain the template in compiled format.
641        """
642        def _readint(term):
643            i = 0
644            while True:
645                c = stream.read(1)
646                if c.isdigit():
647                    i = 10*i+int(c)
648                elif c == term:
649                    return i
650                else:
651                    raise ValueError("invalid terminator, expected %r, got %r" % (term, c))
652
653        def _readstr(term):
654            i = 0
655            digit = False
656            while True:
657                c = stream.read(1)
658                if c.isdigit():
659                    i = 10*i+int(c)
660                    digit = True
661                elif c == term:
662                    if digit:
663                        break
664                    return None
665                else:
666                    raise ValueError("invalid terminator, expected %r, got %r" % (term, c))
667            s = stream.read(i)
668            if len(s) != i:
669                raise ValueError("short read")
670            return s
671
672        def _readspec():
673            c = stream.read(1)
674            if c == "-":
675                return None
676            elif c.isdigit():
677                return int(c)
678            else:
679                raise ValueError("invalid register spec %r" % c)
680
681        def _readcr():
682            c = stream.read(1)
683            if c != "\n":
684                raise ValueError("invalid linefeed %r" % c)
685
686        self = cls()
687        stream = StringIO.StringIO(data)
688        header = stream.readline()
689        header = header.rstrip()
690        if header != "ul4":
691            raise ValueError("invalid header, expected 'ul4', got %r" % header)
692        version = stream.readline()
693        version = version.rstrip()
694        if version != self.version:
695            raise ValueError("invalid version, expected %r got, %r" % (self.version, version))
696        self.startdelim = _readstr(u"<")
697        _readcr()
698        self.enddelim = _readstr(u">")
699        _readcr()
700        self.source = _readstr('"')
701        self.opcodes = []
702        _readcr()
703        count = _readint(u"#")
704        _readcr()
705        location = None
706        while count:
707            r1 = _readspec()
708            r2 = _readspec()
709            r3 = _readspec()
710            r4 = _readspec()
711            r5 = _readspec()
712            code = _readstr(":")
713            arg = _readstr(".")
714            locspec = stream.read(1)
715            if locspec == u"^":
716                if location is None:
717                    raise ValueError("no previous location")
718            elif locspec == u"*":
719                location = Location(self.source, _readstr("="), _readint("("), _readint(")"), _readint("{"), _readint("}"))
720            else:
721                raise ValueError("invalid location spec %r" % locspec)
722            _readcr()
723            count -= 1
724            self.opcodes.append(Opcode(code, r1, r2, r3, r4, r5, arg, location))
725        return self
726
727    @classmethod
728    def load(cls, stream):
729        """
730        The class method :meth:`load` loads the template from the stream
731        :var:`stream`. The stream must contain the template in compiled format.
732        """
733        return cls.loads(stream.read())
734
735    def iterdump(self):
736        """
737        This generator outputs the template in compiled format.
738        """
739        def _writeint(term, number):
740            yield unicode(number)
741            yield term
742
743        def _writestr(term, string):
744            if string is None:
745                yield term
746            else:
747                yield str(len(string))
748                yield term
749                yield string
750
751        yield "ul4\n%s\n" % self.version
752        for p in _writestr("<", self.startdelim): yield p
753        yield "\n"
754        for p in _writestr(">", self.enddelim): yield p
755        yield "\n"
756        for p in _writestr('"', self.source): yield p
757        yield "\n"
758        for p in _writeint("#", len(self.opcodes)): yield p
759        yield "\n"
760        lastlocation = None
761        for opcode in self.opcodes:
762            yield str(opcode.r1) if opcode.r1 is not None else u"-"
763            yield str(opcode.r2) if opcode.r2 is not None else u"-"
764            yield str(opcode.r3) if opcode.r3 is not None else u"-"
765            yield str(opcode.r4) if opcode.r4 is not None else u"-"
766            yield str(opcode.r5) if opcode.r5 is not None else u"-"
767            for p in _writestr(":", opcode.code): yield p
768            for p in _writestr(".", opcode.arg): yield p
769            if opcode.location is not lastlocation:
770                lastlocation = opcode.location
771                yield u"*"
772                for p in _writestr("=", lastlocation.type): yield p
773                for p in _writeint("(", lastlocation.starttag): yield p
774                for p in _writeint(")", lastlocation.endtag): yield p
775                for p in _writeint("{", lastlocation.startcode): yield p
776                for p in _writeint("}", lastlocation.endcode): yield p
777            else:
778                yield "^"
779            yield "\n"
780
781    def dump(self, stream):
782        """
783        :meth:`dump` dumps the template in compiled format to the stream
784        :var:`stream`.
785        """
786        for part in self.iterdump():
787            stream.write(part)
788
789    def dumps(self):
790        """
791        :meth:`dumps` returns the template in compiled format (as a string).
792        """
793        return "".join(self.iterdump())
794
795    def _pythonsource_dispatch_None(self, opcode):
796        self.lines.append("%syield %r" % (self.indent, opcode.location.code))
797
798    def _pythonsource_dispatch_loadstr(self, opcode):
799        self.lines.append("%sreg%d = %r" % (self.indent, opcode.r1, opcode.arg))
800
801    def _pythonsource_dispatch_loadint(self, opcode):
802        self.lines.append("%sreg%d = %s" % (self.indent, opcode.r1, opcode.arg))
803
804    def _pythonsource_dispatch_loadfloat(self, opcode):
805        self.lines.append("%sreg%d = %s" % (self.indent, opcode.r1, opcode.arg))
806
807    def _pythonsource_dispatch_loadnone(self, opcode):
808        self.lines.append("%sreg%d = None" % (self.indent, opcode.r1))
809
810    def _pythonsource_dispatch_loadfalse(self, opcode):
811        self.lines.append("%sreg%d = False" % (self.indent, opcode.r1))
812
813    def _pythonsource_dispatch_loadtrue(self, opcode):
814        self.lines.append("%sreg%d = True" % (self.indent, opcode.r1))
815
816    def _pythonsource_dispatch_loaddate(self, opcode):
817        self.lines.append("%sreg%d = datetime.datetime(%s)" % (self.indent, opcode.r1, ", ".join(str(int(p)) for p in datesplitter.split(opcode.arg))))
818
819    def _pythonsource_dispatch_loadcolor(self, opcode):
820        self.lines.append("%sreg%d = color.Color(0x%s, 0x%s, 0x%s, 0x%s)" % (self.indent, opcode.r1, opcode.arg[:2], opcode.arg[2:4], opcode.arg[4:6], opcode.arg[6:]))
821
822    def _pythonsource_dispatch_buildlist(self, opcode):
823        self.lines.append("%sreg%d = []" % (self.indent, opcode.r1))
824
825    def _pythonsource_dispatch_builddict(self, opcode):
826        self.lines.append("%sreg%d = {}" % (self.indent, opcode.r1))
827
828    def _pythonsource_dispatch_addlist(self, opcode):
829        self.lines.append("%sreg%d.append(reg%d)" % (self.indent, opcode.r1, opcode.r2))
830
831    def _pythonsource_dispatch_adddict(self, opcode):
832        self.lines.append("%sreg%d[reg%d] = reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
833
834    def _pythonsource_dispatch_updatedict(self, opcode):
835        self.lines.append("%sreg%d.update(reg%d)" % (self.indent, opcode.r1, opcode.r2))
836
837    def _pythonsource_dispatch_loadvar(self, opcode):
838        self.lines.append("%sreg%d = variables[%r]" % (self.indent, opcode.r1, opcode.arg))
839
840    def _pythonsource_dispatch_storevar(self, opcode):
841        self.lines.append("%svariables[%r] = reg%d" % (self.indent, opcode.arg, opcode.r1))
842
843    def _pythonsource_dispatch_addvar(self, opcode):
844        self.lines.append("%svariables[%r] += reg%d" % (self.indent, opcode.arg, opcode.r1))
845
846    def _pythonsource_dispatch_subvar(self, opcode):
847        self.lines.append("%svariables[%r] -= reg%d" % (self.indent, opcode.arg, opcode.r1))
848
849    def _pythonsource_dispatch_mulvar(self, opcode):
850        self.lines.append("%svariables[%r] *= reg%d" % (self.indent, opcode.arg, opcode.r1))
851
852    def _pythonsource_dispatch_truedivvar(self, opcode):
853        self.lines.append("%svariables[%r] /= reg%d" % (self.indent, opcode.arg, opcode.r1))
854
855    def _pythonsource_dispatch_floordivvar(self, opcode):
856        self.lines.append("%svariables[%r] //= reg%d" % (self.indent, opcode.arg, opcode.r1))
857
858    def _pythonsource_dispatch_modvar(self, opcode):
859        self.lines.append("%svariables[%r] %%= reg%d" % (self.indent, opcode.arg, opcode.r1))
860
861    def _pythonsource_dispatch_delvar(self, opcode):
862        self.lines.append("%sdel variables[%r]" % (self.indent, opcode.arg))
863
864    def _pythonsource_dispatch_getattr(self, opcode):
865        self.lines.append("%sreg%d = reg%d[%r]" % (self.indent, opcode.r1, opcode.r2, opcode.arg))
866
867    def _pythonsource_dispatch_getitem(self, opcode):
868        self.lines.append("%sreg%d = reg%d[reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
869
870    def _pythonsource_dispatch_getslice12(self, opcode):
871        self.lines.append("%sreg%d = reg%d[reg%d:reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
872
873    def _pythonsource_dispatch_getslice1(self, opcode):
874        self.lines.append("%sreg%d = reg%d[reg%d:]" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
875
876    def _pythonsource_dispatch_getslice2(self, opcode):
877        self.lines.append("%sreg%d = reg%d[:reg%d]" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
878
879    def _pythonsource_dispatch_print(self, opcode):
880        self.lines.append("%sif reg%d is not None: yield unicode(reg%d)" % (self.indent, opcode.r1, opcode.r1))
881
882    def _pythonsource_dispatch_printx(self, opcode):
883        self.lines.append("%sif reg%d is not None: yield xmlescape(unicode(reg%d))" % (self.indent, opcode.r1, opcode.r1))
884
885    def _pythonsource_dispatch_for(self, opcode):
886        self.lines.append("%sfor reg%d in reg%d:" % (self.indent, opcode.r1, opcode.r2))
887        self.indent += "\t"
888
889    def _pythonsource_dispatch_endfor(self, opcode):
890        # we don't have to check for empty loops here, as a ``<?for?>`` tag always generates at least one ``storevar`` opcode inside the loop
891        self.indent = self.indent[:-1]
892        self.lines.append("%s# end for" % self.indent)
893
894    def _pythonsource_dispatch_break(self, opcode):
895        self.lines.append("%sbreak" % self.indent)
896
897    def _pythonsource_dispatch_continue(self, opcode):
898        self.lines.append("%scontinue" % self.indent)
899
900    def _pythonsource_dispatch_not(self, opcode):
901        self.lines.append("%sreg%d = not reg%d" % (self.indent, opcode.r1, opcode.r2))
902
903    def _pythonsource_dispatch_neg(self, opcode):
904        self.lines.append("%sreg%d = -reg%d" % (self.indent, opcode.r1, opcode.r2))
905
906    def _pythonsource_dispatch_contains(self, opcode):
907        self.lines.append("%sreg%d = reg%d in reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
908
909    def _pythonsource_dispatch_notcontains(self, opcode):
910        self.lines.append("%sreg%d = reg%d not in reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
911
912    def _pythonsource_dispatch_eq(self, opcode):
913        self.lines.append("%sreg%d = reg%d == reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
914
915    def _pythonsource_dispatch_ne(self, opcode):
916        self.lines.append("%sreg%d = reg%d != reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
917
918    def _pythonsource_dispatch_lt(self, opcode):
919        self.lines.append("%sreg%d = reg%d < reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
920
921    def _pythonsource_dispatch_le(self, opcode):
922        self.lines.append("%sreg%d = reg%d <= reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
923
924    def _pythonsource_dispatch_gt(self, opcode):
925        self.lines.append("%sreg%d = reg%d > reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
926
927    def _pythonsource_dispatch_ge(self, opcode):
928        self.lines.append("%sreg%d = reg%d >= reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
929
930    def _pythonsource_dispatch_add(self, opcode):
931        self.lines.append("%sreg%d = reg%d + reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
932
933    def _pythonsource_dispatch_sub(self, opcode):
934        self.lines.append("%sreg%d = reg%d - reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
935
936    def _pythonsource_dispatch_mul(self, opcode):
937        self.lines.append("%sreg%d = reg%d * reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
938
939    def _pythonsource_dispatch_floordiv(self, opcode):
940        self.lines.append("%sreg%d = reg%d // reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
941
942    def _pythonsource_dispatch_truediv(self, opcode):
943        self.lines.append("%sreg%d = reg%d / reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
944
945    def _pythonsource_dispatch_and(self, opcode):
946        self.lines.append("%sreg%d = reg%d and reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
947
948    def _pythonsource_dispatch_or(self, opcode):
949        self.lines.append("%sreg%d = reg%d or reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
950
951    def _pythonsource_dispatch_mod(self, opcode):
952        self.lines.append("%sreg%d = reg%d %% reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
953
954    def _pythonsource_dispatch_mod(self, opcode):
955        self.lines.append("%sreg%d = reg%d %% reg%d" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
956
957    def _pythonsource_dispatch_callfunc0(self, opcode):
958        try:
959            getattr(self, "_pythonsource_dispatch_callfunc0_%s" % opcode.arg)(opcode)
960        except AttributeError:
961            raise UnknownFunctionError(opcode.arg)
962
963    def _pythonsource_dispatch_callfunc1(self, opcode):
964        try:
965            getattr(self, "_pythonsource_dispatch_callfunc1_%s" % opcode.arg)(opcode)
966        except AttributeError:
967            raise UnknownFunctionError(opcode.arg)
968
969    def _pythonsource_dispatch_callfunc2(self, opcode):
970        try:
971            getattr(self, "_pythonsource_dispatch_callfunc2_%s" % opcode.arg)(opcode)
972        except AttributeError:
973            raise UnknownFunctionError(opcode.arg)
974
975    def _pythonsource_dispatch_callfunc3(self, opcode):
976        try:
977            getattr(self, "_pythonsource_dispatch_callfunc3_%s" % opcode.arg)(opcode)
978        except AttributeError:
979            raise UnknownFunctionError(opcode.arg)
980
981    def _pythonsource_dispatch_callfunc4(self, opcode):
982        try:
983            getattr(self, "_pythonsource_dispatch_callfunc4_%s" % opcode.arg)(opcode)
984        except AttributeError:
985            raise UnknownFunctionError(opcode.arg)
986
987    def _pythonsource_dispatch_callmeth0(self, opcode):
988        if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "upper", "lower", "capitalize", "isoformat", "r", "g", "b", "a", "hls", "hlsa", "hsv", "hsva", "lum"):
989            self.lines.append("%sreg%d = reg%d.%s()" % (self.indent, opcode.r1, opcode.r2, opcode.arg))
990        elif opcode.arg == "items":
991            self.lines.append("%sreg%d = reg%d.iteritems()" % (self.indent, opcode.r1, opcode.r2))
992        elif opcode.arg == "render":
993            self.lines.append('%sreg%d = "".join(reg%d())' % (self.indent, opcode.r1, opcode.r2))
994        else:
995            raise UnknownMethodError(opcode.arg)
996
997    def _pythonsource_dispatch_callmeth1(self, opcode):
998        if opcode.arg in ("split", "rsplit", "strip", "lstrip", "rstrip", "startswith", "endswith", "find", "get", "withlum", "witha"):
999            self.lines.append("%sreg%d = reg%d.%s(reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3))
1000        elif opcode.arg == "join":
1001            self.lines.append("%sreg%d = reg%d.join(unicode(x) for x in reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1002        elif opcode.arg == "format":
1003            self.lines.append("%sreg%d = ul4c._format(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1004        else:
1005            raise UnknownMethodError(opcode.arg)
1006
1007    def _pythonsource_dispatch_callmeth2(self, opcode):
1008        if opcode.arg in ("split", "rsplit", "find", "replace", "get"):
1009            self.lines.append("%sreg%d = reg%d.%s(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4))
1010        else:
1011            raise UnknownMethodError(opcode.arg)
1012
1013    def _pythonsource_dispatch_callmeth3(self, opcode):
1014        if opcode.arg == "find":
1015            self.lines.append("%sreg%d = reg%d.%s(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.arg, opcode.r3, opcode.r4, opcode.r5))
1016        else:
1017            raise UnknownMethodError(opcode.arg)
1018
1019    def _pythonsource_dispatch_callmethkw(self, opcode):
1020        if opcode.arg == "render":
1021            self.lines.append('%sreg%d = "".join(reg%d(**dict((key.encode("utf-8"), value) for (key, value) in reg%d.iteritems())))' % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1022        else:
1023            raise UnknownMethodError(opcode.arg)
1024
1025    def _pythonsource_dispatch_if(self, opcode):
1026        self.lines.append("%sif reg%d:" % (self.indent, opcode.r1))
1027        self.indent += "\t"
1028
1029    def _pythonsource_dispatch_else(self, opcode):
1030        if self.lastopcode == "if":
1031            self.lines[-1] += " pass"
1032        self.indent = self.indent[:-1]
1033        self.lines.append("%selse:" % self.indent)
1034        self.indent += "\t"
1035
1036    def _pythonsource_dispatch_endif(self, opcode):
1037        if self.lastopcode in ("if", "else"):
1038            self.lines[-1] += " pass"
1039        self.indent = self.indent[:-1]
1040        self.lines.append("%s# end if" % self.indent)
1041
1042    def _pythonsource_dispatch_render(self, opcode):
1043        self.lines.append('%sfor chunk in reg%d(**dict((key.encode("utf-8"), value) for (key, value) in reg%d.iteritems())): yield chunk' % (self.indent, opcode.r1, opcode.r2))
1044
1045    def _pythonsource_dispatch_callfunc0_now(self, opcode):
1046        self.lines.append("%sreg%d = datetime.datetime.now()" % (self.indent, opcode.r1))
1047
1048    def _pythonsource_dispatch_callfunc0_vars(self, opcode):
1049        self.lines.append("%sreg%d = variables" % (self.indent, opcode.r1))
1050
1051    def _pythonsource_dispatch_callfunc1_xmlescape(self, opcode):
1052        self.lines.append("%sreg%d = xmlescape(unicode(reg%d)) if reg%d is not None else u''" % (self.indent, opcode.r1, opcode.r2, opcode.r2))
1053
1054    def _pythonsource_dispatch_callfunc1_csv(self, opcode):
1055        self.lines.append("%sreg%d = ul4c._csv(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1056
1057    def _pythonsource_dispatch_callfunc1_json(self, opcode):
1058        self.lines.append("%sreg%d = json.dumps(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1059
1060    def _pythonsource_dispatch_callfunc1_str(self, opcode):
1061        self.lines.append("%sreg%d = unicode(reg%d) if reg%d is not None else u''" % (self.indent, opcode.r1, opcode.r2, opcode.r2))
1062
1063    def _pythonsource_dispatch_callfunc1_int(self, opcode):
1064        self.lines.append("%sreg%d = int(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1065
1066    def _pythonsource_dispatch_callfunc1_bool(self, opcode):
1067        self.lines.append("%sreg%d = bool(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1068
1069    def _pythonsource_dispatch_callfunc1_len(self, opcode):
1070        self.lines.append("%sreg%d = len(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1071
1072    def _pythonsource_dispatch_callfunc1_enumerate(self, opcode):
1073        self.lines.append("%sreg%d = enumerate(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1074
1075    def _pythonsource_dispatch_callfunc1_isnone(self, opcode):
1076        self.lines.append("%sreg%d = reg%d is None" % (self.indent, opcode.r1, opcode.r2))
1077
1078    def _pythonsource_dispatch_callfunc1_isstr(self, opcode):
1079        self.lines.append("%sreg%d = isinstance(reg%d, basestring)" % (self.indent, opcode.r1, opcode.r2))
1080
1081    def _pythonsource_dispatch_callfunc1_isint(self, opcode):
1082        self.lines.append("%sreg%d = isinstance(reg%d, (int, long)) and not isinstance(reg%d, bool)" % (self.indent, opcode.r1, opcode.r2, opcode.r2))
1083
1084    def _pythonsource_dispatch_callfunc1_isfloat(self, opcode):
1085        self.lines.append("%sreg%d = isinstance(reg%d, float)" % (self.indent, opcode.r1, opcode.r2))
1086
1087    def _pythonsource_dispatch_callfunc1_isbool(self, opcode):
1088        self.lines.append("%sreg%d = isinstance(reg%d, bool)" % (self.indent, opcode.r1, opcode.r2))
1089
1090    def _pythonsource_dispatch_callfunc1_isdate(self, opcode):
1091        self.lines.append("%sreg%d = isinstance(reg%d, datetime.datetime)" % (self.indent, opcode.r1, opcode.r2))
1092
1093    def _pythonsource_dispatch_callfunc1_islist(self, opcode):
1094        self.lines.append("%sreg%d = isinstance(reg%d, (list, tuple))" % (self.indent, opcode.r1, opcode.r2))
1095
1096    def _pythonsource_dispatch_callfunc1_isdict(self, opcode):
1097        self.lines.append("%sreg%d = isinstance(reg%d, dict)" % (self.indent, opcode.r1, opcode.r2))
1098
1099    def _pythonsource_dispatch_callfunc1_istemplate(self, opcode):
1100        self.lines.append("%sreg%d = hasattr(reg%d, '__call__')" % (self.indent, opcode.r1, opcode.r2)) # this supports normal generators too
1101
1102    def _pythonsource_dispatch_callfunc1_repr(self, opcode):
1103        self.lines.append("%sreg%d = ul4c._repr(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1104
1105    def _pythonsource_dispatch_callfunc1_get(self, opcode):
1106        self.lines.append("%sreg%d = variables.get(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1107
1108    def _pythonsource_dispatch_callfunc1_chr(self, opcode):
1109        self.lines.append("%sreg%d = unichr(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1110
1111    def _pythonsource_dispatch_callfunc1_ord(self, opcode):
1112        self.lines.append("%sreg%d = ord(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1113
1114    def _pythonsource_dispatch_callfunc1_hex(self, opcode):
1115        self.lines.append("%sreg%d = hex(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1116
1117    def _pythonsource_dispatch_callfunc1_oct(self, opcode):
1118        self.lines.append("%sreg%d = ul4c._oct(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1119
1120    def _pythonsource_dispatch_callfunc1_bin(self, opcode):
1121        self.lines.append("%sreg%d = ul4c._bin(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1122
1123    def _pythonsource_dispatch_callfunc1_sorted(self, opcode):
1124        self.lines.append("%sreg%d = sorted(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1125
1126    def _pythonsource_dispatch_callfunc1_range(self, opcode):
1127        self.lines.append("%sreg%d = xrange(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1128
1129    def _pythonsource_dispatch_callfunc1_type(self, opcode):
1130        self.lines.append("%sreg%d = ul4c._type(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1131
1132    def _pythonsource_dispatch_callfunc1_reversed(self, opcode):
1133        self.lines.append("%sreg%d = reversed(reg%d)" % (self.indent, opcode.r1, opcode.r2))
1134
1135    def _pythonsource_dispatch_callfunc2_range(self, opcode):
1136        self.lines.append("%sreg%d = xrange(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1137
1138    def _pythonsource_dispatch_callfunc2_get(self, opcode):
1139        self.lines.append("%sreg%d = variables.get(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1140
1141    def _pythonsource_dispatch_callfunc2_zip(self, opcode):
1142        self.lines.append("%sreg%d = itertools.izip(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1143
1144    def _pythonsource_dispatch_callfunc2_int(self, opcode):
1145        self.lines.append("%sreg%d = int(reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3))
1146
1147    def _pythonsource_dispatch_callfunc3_range(self, opcode):
1148        self.lines.append("%sreg%d = xrange(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
1149
1150    def _pythonsource_dispatch_callfunc3_zip(self, opcode):
1151        self.lines.append("%sreg%d = itertools.izip(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
1152
1153    def _pythonsource_dispatch_callfunc3_rgb(self, opcode):
1154        self.lines.append("%sreg%d = color.Color.fromrgb(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
1155
1156    def _pythonsource_dispatch_callfunc3_hls(self, opcode):
1157        self.lines.append("%sreg%d = color.Color.fromhls(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
1158
1159    def _pythonsource_dispatch_callfunc3_hsv(self, opcode):
1160        self.lines.append("%sreg%d = color.Color.fromhsv(reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4))
1161
1162    def _pythonsource_dispatch_callfunc4_rgb(self, opcode):
1163        self.lines.append("%sreg%d = color.Color.fromrgb(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5))
1164
1165    def _pythonsource_dispatch_callfunc4_hls(self, opcode):
1166        self.lines.append("%sreg%d = color.Color.fromhls(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5))
1167
1168    def _pythonsource_dispatch_callfunc4_hsv(self, opcode):
1169        self.lines.append("%sreg%d = color.Color.fromhsv(reg%d, reg%d, reg%d, reg%d)" % (self.indent, opcode.r1, opcode.r2, opcode.r3, opcode.r4, opcode.r5))
1170
1171    def pythonsource(self, function=None):
1172        """
1173        Return the template as Python source code. If :var:`function` is specified
1174        the code will be wrapped in a function with this name.
1175        """
1176        self.indent = ""
1177        self.lines = []
1178
1179        if function is not None:
1180            self.lines.append("%sdef %s(**variables):" % (self.indent, function))
1181            self.indent += "\t"
1182        self.lines.append("%simport sys, datetime, itertools" % self.indent)
1183        self.lines.append("%stry:" % self.indent)
1184        self.indent += "\t"
1185        self.lines.append("%simport json" % self.indent)
1186        self.indent = self.indent[:-1]
1187        self.lines.append("%sexcept ImportError:" % self.indent)
1188        self.indent += "\t"
1189        self.lines.append("%stry:" % self.indent)
1190        self.indent += "\t"
1191        self.lines.append("%simport simplejson as json" % self.indent)
1192        self.indent = self.indent[:-1]
1193        self.lines.append("%sexcept ImportError:" % self.indent)
1194        self.indent += "\t"
1195        self.lines.append("%spass" % self.indent)
1196        self.indent = self.indent[:-2]
1197        self.lines.append("%sfrom ll.misc import xmlescape" % self.indent)
1198        self.lines.append("%sfrom ll import ul4c, color" % self.indent)
1199        self.lines.append("%ssource = %r" % (self.indent, self.source))
1200        self.lines.append('%svariables = dict((key.decode("utf-8"), value) for (key, value) in variables.iteritems())' % self.indent) # FIXME: This can be dropped in Python 3.0 where strings are unicode
1201        # Make sure that the resulting code is a generator even if the byte codes produces no yield statement
1202        self.lines.append("%sif 0: yield ''" % self.indent)
1203        locations = []
1204        lines2locs = []
1205        index = -1
1206        for oc in self.opcodes:
1207            loc = (oc.location.type, oc.location.starttag, oc.location.endtag, oc.location.startcode, oc.location.endcode)
1208            if not locations or locations[-1] != loc:
1209                locations.append(loc)
1210                index += 1
1211            lines2locs.append(index)
1212        locations = tuple(locations)
1213        lines2locs = tuple(lines2locs)
1214        self.lines.append("%sreg0 = reg1 = reg2 = reg3 = reg4 = reg5 = reg6 = reg7 = reg8 = reg9 = None" % self.indent)
1215        self.lines.append("%stry:" % self.indent)
1216        self.indent += "\t"
1217        self.lines.append("%sstartline = sys._getframe().f_lineno+1" % self.indent) # The source line of the first opcode
1218        try:
1219            self.lastopcode = None
1220            for opcode in self.opcodes:
1221                # The following code ensures that each opcode outputs exactly one source code line
1222                # This makes it possible in case of an error to find out which opcode produced the error
1223                try:
1224                    getattr(self, "_pythonsource_dispatch_%s" % opcode.code)(opcode)
1225                except AttributeError:
1226                    raise UnknownOpcodeError(opcode.code)
1227                self.lastopcode = opcode.code
1228        except Exception, exc:
1229            raise #Error(opcode.location, exc)
1230        self.indent = self.indent[:-1]
1231        self.lines.append("%sexcept Exception, exc:" % self.indent)
1232        self.indent += "\t"
1233        self.lines.append("%slocations = %r" % (self.indent, locations))
1234        self.lines.append("%slines2locs = %r" % (self.indent, lines2locs))
1235        self.lines.append("%sraise ul4c.Error(ul4c.Location(source, *locations[lines2locs[sys.exc_info()[2].tb_lineno-startline]]), exc)" % self.indent)
1236        result = "\n".join(self.lines)
1237        del self.lines
1238        del self.indent
1239        return result
1240
1241    def pythonfunction(self):
1242        """
1243        Return a Python generator that can be called to render the template. The
1244        argument signature of the function will be ``**variables``.
1245        """
1246        if self._pythonfunction is None:
1247            code = self.pythonsource("render")
1248            ns = {}
1249            exec code.encode("utf-8") in ns # FIXME: no need to encode in Python 3.0
1250            self._pythonfunction = ns["render"]
1251        return self._pythonfunction
1252
1253    def __call__(self, **variables):
1254        return self.pythonfunction()(**variables)
1255
1256    def render(self, **variables):
1257        """
1258        Render the template iteratively (i.e. this is a generator).
1259        :var:`variables` contains the top level variables available to the
1260        template code.
1261        """
1262        return self.pythonfunction()(**variables)
1263
1264    def renders(self, **variables):
1265        """
1266        Render the template as a string. :var:`variables` contains the top level
1267        variables available to the template code.
1268        """
1269        return "".join(self.render(**variables))
1270
1271    def format(self, indent="\t"):
1272        """
1273        Format the list of opcodes. This is a generator yielding lines to be output
1274        (but without trailing newlines). :var:`indent` can be used to specify how
1275        to indent blocks (defaulting to ``"\\t"``).
1276        """
1277        i = 0
1278        for opcode in self.opcodes:
1279            if opcode.code in ("else", "endif", "endfor"):
1280                i -= 1
1281            if opcode.code in ("endif", "endfor"):
1282                yield "%s}" % (i*indent)
1283            elif opcode.code in ("for", "if"):
1284                yield "%s%s {" % (i*indent, opcode)
1285            elif opcode.code == "else":
1286                yield "%s} else {" % (i*indent)
1287            else:
1288                yield "%s%s" % (i*indent, opcode)
1289            if opcode.code in ("for", "if", "else"):
1290                i += 1
1291
1292    def _tokenize(self, source, startdelim, enddelim):
1293        """
1294        Tokenize the template source code :var:`source` into tags and non-tag
1295        text. :var:`startdelim` and :var:`enddelim` are used as the tag delimiters.
1296
1297        This is a generator which produces :class:`Location` objects for each tag
1298        or non-tag text. It will be called by :meth:`_compile` internally.
1299        """
1300        pattern = u"%s(printx|print|code|for|if|elif|else|end|break|continue|render|note)(\s*((.|\\n)*?)\s*)?%s" % (re.escape(startdelim), re.escape(enddelim))
1301        pos = 0
1302        for match in re.finditer(pattern, source):
1303            if match.start() != pos:
1304                yield Location(source, None, pos, match.start(), pos, match.start())
1305            type = source[match.start(1):match.end(1)]
1306            if type != "note":
1307                yield Location(source, type, match.start(), match.end(), match.start(3), match.end(3))
1308            pos = match.end()
1309        end = len(source)
1310        if pos != end:
1311            yield Location(source, None, pos, end, pos, end)
1312
1313    def _allocreg(self):
1314        """
1315        Allocates a free register from the pool of available registers.
1316        """
1317        try:
1318            return self.registers.pop()
1319        except KeyError:
1320            raise OutOfRegistersError()
1321
1322    def _freereg(self, register):
1323        """
1324        Returns the register :var:`register` to the pool of available registers.
1325        """
1326        self.registers.add(register)
1327
1328    def opcode(self, code, r1=None, r2=None, r3=None, r4=None, r5=None, arg=None):
1329        """
1330        Creates an :class:`Opcode` object and appends it to :var:`self`\s list of
1331        opcodes.
1332        """
1333        self.opcodes.append(Opcode(code, r1, r2, r3, r4, r5, arg, self.location))
1334
1335    def _compile(self, source, startdelim, enddelim):
1336        """
1337        Compile the template source code :var:`source` into opcodes.
1338        :var:`startdelim` and :var:`enddelim` are used as the tag delimiters.
1339        """
1340        self.startdelim = startdelim
1341        self.enddelim = enddelim
1342        scanner = Scanner()
1343        parseexpr = ExprParser(scanner).compile
1344        parsestmt = StmtParser(scanner).compile
1345        parsefor = ForParser(scanner).compile
1346        parserender = RenderParser(scanner).compile
1347
1348        # This stack stores for each nested for/foritem/if/elif/else the following information:
1349        # 1) Which construct we're in (i.e. "if" or "for")
1350        # 2) The start location of the construct
1351        # For ifs:
1352        # 3) How many if's or elif's we have seen (this is used for simulating elif's via nested if's, for each additional elif, we have one more endif to add)
1353        # 4) Whether we've already seen the else
1354        stack = []
1355
1356        self.source = source
1357        self.opcodes = []
1358
1359        for location in self._tokenize(source, startdelim, enddelim):
1360            self.location = location
1361            try:
1362                if location.type is None:
1363                    self.opcode(None)
1364                elif location.type == "print":
1365                    r = parseexpr(self)
1366                    self.opcode("print", r1=r)
1367                elif location.type == "printx":
1368                    r = parseexpr(self)
1369                    self.opcode("printx", r1=r)
1370                elif location.type == "code":
1371                    parsestmt(self)
1372                elif location.type == "if":
1373                    r = parseexpr(self)
1374                    self.opcode("if", r1=r)
1375                    stack.append(("if", location, 1, False))
1376                elif location.type == "elif":
1377                    if not stack or stack[-1][0] != "if":
1378                        raise BlockError("elif doesn't match any if")
1379                    elif stack[-1][3]:
1380                        raise BlockError("else already seen in elif")
1381                    self.opcode("else")
1382                    r = parseexpr(self)
1383                    self.opcode("if", r1=r)
1384                    stack[-1] = ("if", stack[-1][1], stack[-1][2]+1, False)
1385                elif location.type == "else":
1386                    if not stack or stack[-1][0] != "if":
1387                        raise BlockError("else doesn't match any if")
1388                    elif stack[-1][3]:
1389                        raise BlockError("duplicate else")
1390                    self.opcode("else")
1391                    stack[-1] = ("if", stack[-1][1], stack[-1][2], True)
1392                elif location.type == "end":
1393                    if not stack:
1394                        raise BlockError("not in any block")
1395                    code = location.code
1396                    if code:
1397                        if code == "if":
1398                            if stack[-1][0] != "if":
1399                                raise BlockError("endif doesn't match any if")
1400                        elif code == "for":
1401                            if stack[-1][0] != "for":
1402                                raise BlockError("endfor doesn't match any for")
1403                        else:
1404                            raise BlockError("illegal end value %r" % code)
1405                    last = stack.pop()
1406                    if last[0] == "if":
1407                        for i in xrange(last[2]):
1408                            self.opcode("endif")
1409                    else: # last[0] == "for":
1410                        self.opcode("endfor")
1411                elif location.type == "for":
1412                    parsefor(self)
1413                    stack.append(("for", location))
1414                elif location.type == "break":
1415                    if not any(entry[0] == "for" for entry in stack):
1416                        raise BlockError("break outside of for loop")
1417                    self.opcode("break")
1418                elif location.type == "continue":
1419                    if not any(entry[0] == "for" for entry in stack):
1420                        raise BlockError("continue outside of for loop")
1421                    self.opcode("continue")
1422                elif location.type == "render":
1423                    parserender(self)
1424                else: # Can't happen
1425                    raise ValueError("unknown tag %r" % location.type)
1426            except Exception, exc:
1427                raise Error(location, exc)
1428            finally:
1429                del self.location
1430        if stack:
1431            raise Error(stack[-1][1], BlockError("block unclosed"))
1432
1433    def __str__(self):
1434        return "\n".join(self.format())
1435
1436    def __unicode__(self):
1437        return u"\n".join(self.format())
1438
1439    def __repr__(self):
1440        return "<%s.%s object with %d opcodes at 0x%x>" % (self.__class__.__module__, self.__class__.__name__, len(self.opcodes), id(self))
1441
1442
1443def compile(source, startdelim="<?", enddelim="?>"):
1444    template = Template()
1445    template._compile(source, startdelim, enddelim)
1446    return template
1447
1448load = Template.load
1449loads = Template.loads
1450
1451
1452###
1453### Tokens and nodes for the AST
1454###
1455
1456class Token(object):
1457    def __init__(self, start, end, type):
1458        self.start = start
1459        self.end = end
1460        self.type = type
1461
1462    def __repr__(self):
1463        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.type)
1464
1465    def __str__(self):
1466        return self.type
1467
1468
1469class AST(object):
1470    """
1471    Baseclass for all syntax tree nodes.
1472    """
1473
1474    def __init__(self, start, end):
1475        self.start = start
1476        self.end = end
1477
1478
1479class Const(AST):
1480    """
1481    Common baseclass for all constants (used for type testing in constant folding)
1482    """
1483
1484    def __repr__(self):
1485        return "%s(%r, %r)" % (self.__class__.__name__, self.start, self.end)
1486
1487    def compile(self, template):
1488        r = template._allocreg()
1489        template.opcode("load%s" % self.type, r1=r)
1490        return r
1491
1492
1493class None_(Const):
1494    type = "none"
1495    value = None
1496
1497
1498class True_(Const):
1499    type = "true"
1500    value = True
1501
1502
1503class False_(Const):
1504    type = "false"
1505    value = False
1506
1507
1508class Value(Const):
1509    def __init__(self, start, end, value):
1510        Const.__init__(self, start, end)
1511        self.value = value
1512
1513    def __repr__(self):
1514        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.value)
1515
1516    def compile(self, template):
1517        r = template._allocreg()
1518        template.opcode("load%s" % self.type, r1=r, arg=unicode(self.value))
1519        return r
1520
1521
1522class Int(Value):
1523    type = "int"
1524
1525
1526class Float(Value):
1527    type = "float"
1528
1529    def compile(self, template):
1530        r = template._allocreg()
1531        template.opcode("load%s" % self.type, r1=r, arg=repr(self.value))
1532        return r
1533
1534
1535class Str(Value):
1536    type = "str"
1537
1538
1539class Date(Value):
1540    type = "date"
1541
1542    def compile(self, template):
1543        r = template._allocreg()
1544        template.opcode("load%s" % self.type, r1=r, arg=self.value.isoformat())
1545        return r
1546
1547
1548class Color(Value):
1549    type = "color"
1550
1551    def compile(self, template):
1552        r = template._allocreg()
1553        template.opcode("load%s" % self.type, r1=r, arg="%02x%02x%02x%02x" % self.value)
1554        return r
1555
1556
1557class List(AST):
1558    def __init__(self, start, end, *items):
1559        AST.__init__(self, start, end)
1560        self.items = list(items)
1561
1562    def __repr__(self):
1563        return "%s(%r, %r, %s)" % (self.__class__.__name__, self.start, self.end, repr(self.items)[1:-1])
1564
1565    def compile(self, template):
1566        r = template._allocreg()
1567        template.opcode("buildlist", r1=r)
1568        for item in self.items:
1569            ri = item.compile(template)
1570            template.opcode("addlist", r1=r, r2=ri)
1571            template._freereg(ri)
1572        return r
1573
1574
1575class Dict(AST):
1576    def __init__(self, start, end, *items):
1577        AST.__init__(self, start, end)
1578        self.items = list(items)
1579
1580    def __repr__(self):
1581        return "%s(%r, %r, %s)" % (self.__class__.__name__, self.start, self.end, repr(self.items)[1:-1])
1582
1583    def compile(self, template):
1584        r = template._allocreg()
1585        template.opcode("builddict", r1=r)
1586        for item in self.items:
1587            if len(item) == 1:
1588                rd = item[0].compile(template)
1589                template.opcode("updatedict", r1=r, r2=rd)
1590                template._freereg(rd)
1591            else:
1592                (key, value) = item
1593                rk = key.compile(template)
1594                rv = value.compile(template)
1595                template.opcode("adddict", r1=r, r2=rk, r3=rv)
1596                template._freereg(rk)
1597                template._freereg(rv)
1598        return r
1599
1600
1601class Name(AST):
1602    type = "name"
1603
1604    def __init__(self, start, end, name):
1605        AST.__init__(self, start, end)
1606        self.name = name
1607
1608    def __repr__(self):
1609        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name)
1610
1611    def compile(self, template):
1612        r = template._allocreg()
1613        template.opcode("loadvar", r1=r, arg=self.name)
1614        return r
1615
1616
1617class For(AST):
1618    def __init__(self, start, end, iter, cont):
1619        AST.__init__(self, start, end)
1620        self.iter = iter
1621        self.cont = cont
1622
1623    def __repr__(self):
1624        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.iter, self.cont)
1625
1626    def compile(self, template):
1627        rc = self.cont.compile(template)
1628        ri = template._allocreg()
1629        template.opcode("for", r1=ri, r2=rc)
1630        if isinstance(self.iter, list):
1631            for (i, iter) in enumerate(self.iter):
1632                rii = template._allocreg()
1633                template.opcode("loadint", r1=rii, arg=str(i))
1634                template.opcode("getitem", r1=rii, r2=ri, r3=rii)
1635                template.opcode("storevar", r1=rii, arg=iter.name)
1636                template._freereg(rii)
1637        else:
1638            template.opcode("storevar", r1=ri, arg=self.iter.name)
1639        template._freereg(ri)
1640        template._freereg(rc)
1641
1642
1643class GetAttr(AST):
1644    def __init__(self, start, end, obj, attr):
1645        AST.__init__(self, start, end)
1646        self.obj = obj
1647        self.attr = attr
1648
1649    def __repr__(self):
1650        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.obj, self.attr)
1651
1652    def compile(self, template):
1653        r = self.obj.compile(template)
1654        template.opcode("getattr", r1=r, r2=r, arg=self.attr.name)
1655        return r
1656
1657
1658class GetSlice12(AST):
1659    def __init__(self, start, end, obj, index1, index2):
1660        AST.__init__(self, start, end)
1661        self.obj = obj
1662        self.index1 = index1
1663        self.index2 = index2
1664
1665    def __repr__(self):
1666        return "%s(%r, %r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.obj, self.index1, self.index2)
1667
1668    def compile(self, template):
1669        r1 = self.obj.compile(template)
1670        r2 = self.index1.compile(template)
1671        r3 = self.index2.compile(template)
1672        template.opcode("getslice12", r1=r1, r2=r1, r3=r2, r4=r3)
1673        template._freereg(r2)
1674        template._freereg(r3)
1675        return r1
1676
1677
1678class Unary(AST):
1679    opcode = None
1680
1681    def __init__(self, start, end, obj):
1682        AST.__init__(self, start, end)
1683        self.obj = obj
1684
1685    def __repr__(self):
1686        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.obj)
1687
1688    def compile(self, template):
1689        r = self.obj.compile(template)
1690        template.opcode(self.opcode, r1=r, r2=r)
1691        return r
1692
1693
1694class Not(Unary):
1695    opcode = "not"
1696
1697
1698class Neg(Unary):
1699    opcode = "neg"
1700
1701
1702class Binary(AST):
1703    opcode = None
1704
1705    def __init__(self, start, end, obj1, obj2):
1706        AST.__init__(self, start, end)
1707        self.obj1 = obj1
1708        self.obj2 = obj2
1709
1710    def __repr__(self):
1711        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.obj1, self.obj2)
1712
1713    def compile(self, template):
1714        r1 = self.obj1.compile(template)
1715        r2 = self.obj2.compile(template)
1716        template.opcode(self.opcode, r1=r1, r2=r1, r3=r2)
1717        template._freereg(r2)
1718        return r1
1719
1720
1721class GetItem(Binary):
1722    opcode = "getitem"
1723
1724
1725class GetSlice1(Binary):
1726    opcode = "getslice1"
1727
1728
1729class GetSlice2(Binary):
1730    opcode = "getslice2"
1731
1732
1733class EQ(Binary):
1734    opcode = "eq"
1735
1736
1737class NE(Binary):
1738    opcode = "ne"
1739
1740
1741class LT(Binary):
1742    opcode = "lt"
1743
1744
1745class LE(Binary):
1746    opcode = "le"
1747
1748
1749class GT(Binary):
1750    opcode = "gt"
1751
1752
1753class GE(Binary):
1754    opcode = "ge"
1755
1756
1757class Contains(Binary):
1758    opcode = "contains"
1759
1760
1761class NotContains(Binary):
1762    opcode = "notcontains"
1763
1764
1765class Add(Binary):
1766    opcode = "add"
1767
1768
1769class Sub(Binary):
1770    opcode = "sub"
1771
1772
1773class Mul(Binary):
1774    opcode = "mul"
1775
1776
1777class FloorDiv(Binary):
1778    opcode = "floordiv"
1779
1780
1781class TrueDiv(Binary):
1782    opcode = "truediv"
1783
1784
1785class Or(Binary):
1786    opcode = "or"
1787
1788
1789class And(Binary):
1790    opcode = "and"
1791
1792
1793class Mod(Binary):
1794    opcode = "mod"
1795
1796
1797class ChangeVar(AST):
1798    opcode = None
1799
1800    def __init__(self, start, end, name, value):
1801        AST.__init__(self, start, end)
1802        self.name = name
1803        self.value = value
1804
1805    def __repr__(self):
1806        return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name, self.value)
1807
1808    def compile(self, template):
1809        r = self.value.compile(template)
1810        template.opcode(self.opcode, r1=r, arg=self.name.name)
1811        template._freereg(r)
1812
1813
1814class StoreVar(ChangeVar):
1815    opcode = "storevar"
1816
1817
1818class AddVar(ChangeVar):
1819    opcode = "addvar"
1820
1821
1822class SubVar(ChangeVar):
1823    opcode = "subvar"
1824
1825
1826class MulVar(ChangeVar):
1827    opcode = "mulvar"
1828
1829
1830class TrueDivVar(ChangeVar):
1831    opcode = "truedivvar"
1832
1833
1834class FloorDivVar(ChangeVar):
1835    opcode = "floordivvar"
1836
1837
1838class ModVar(ChangeVar):
1839    opcode = "modvar"
1840
1841
1842class DelVar(AST):
1843    def __init__(self, start, end, name):
1844        AST.__init__(self, start, end)
1845        self.name = name
1846
1847    def __repr__(self):
1848        return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name)
1849
1850    def compile(self, template):
1851        template.opcode("delvar", arg=self.name.name)
1852
1853
1854class CallFunc(AST):
1855    def __init__(self, start, end, name, args):
1856        AST.__init__(self, start, end)
1857        self.name = name
1858        self.args = args
1859
1860    def __repr__(self):
1861        if self.args:
1862            return "%s(%r, %r, %r, %s)" % (self.__class__.__name__, self.start, self.end, self.name, repr(self.args)[1:-1])
1863        else:
1864            return "%s(%r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name)
1865
1866    def compile(self, template):
1867        if len(self.args) == 0:
1868            r = template._allocreg()
1869            template.opcode("callfunc0", r1=r, arg=self.name.name)
1870            return r
1871        elif len(self.args) > 4:
1872            raise ValueError("%d function arguments not supported" % len(self.args))
1873        else:
1874            rs = [arg.compile(template) for arg in self.args]
1875            template.opcode("callfunc%d" % len(self.args), rs[0], *rs, **dict(arg=self.name.name)) # FIXME: Replace **dict(arg=) with arg= in Python 2.6?
1876            for i in xrange(1, len(self.args)):
1877                template._freereg(rs[i])
1878            return rs[0]
1879
1880
1881class CallMeth(AST):
1882    def __init__(self, start, end, name, obj, args):
1883        AST.__init__(self, start, end)
1884        self.name = name
1885        self.obj = obj
1886        self.args = args
1887
1888    def __repr__(self):
1889        if self.args:
1890            return "%s(%r, %r, %r, %r, %s)" % (self.__class__.__name__, self.start, self.end, self.name, self.obj, repr(self.args)[1:-1])
1891        else:
1892            return "%s(%r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name, self.obj)
1893
1894    def compile(self, template):
1895        if len(self.args) > 3:
1896            raise ValueError("%d method arguments not supported" % len(self.args))
1897        ro = self.obj.compile(template)
1898        rs = [arg.compile(template) for arg in self.args]
1899        template.opcode("callmeth%d" % len(self.args), ro, ro, *rs, **dict(arg=self.name.name))
1900        for r in rs:
1901            template._freereg(r)
1902        return ro
1903
1904
1905class CallMethKeywords(AST):
1906    def __init__(self, start, end, name, obj, args):
1907        AST.__init__(self, start, end)
1908        self.name = name
1909        self.obj = obj
1910        self.args = args
1911
1912    def __repr__(self):
1913        return "%s(%r, %r, %r, %r, %r)" % (self.__class__.__name__, self.start, self.end, self.name, self.obj, self.args)
1914
1915    def compile(self, template):
1916        ra = template._allocreg()
1917        template.opcode("builddict", r1=ra)
1918        for item in self.args:
1919            if len(item) == 1:
1920                rd = item[0].compile(template)
1921                template.opcode("updatedict", r1=ra, r2=rd)
1922                template._freereg(rd)
1923            else:
1924                (key, value) = item
1925                rv = value.compile(template)
1926                rk = template._allocreg()
1927                template.opcode("loadstr", r1=rk, arg=key.name)
1928                template.opcode("adddict", r1=ra, r2=rk, r3=rv)
1929                template._freereg(rk)
1930                template._freereg(rv)
1931        ro = self.obj.compile(template)
1932        template.opcode("callmethkw", r1=ro, r2=ro, r3=ra, arg=self.name.name)
1933        template._freereg(ra)
1934        return ro
1935
1936
1937class Render(AST):
1938    def __init__(self, start, end, template, *variables):
1939        AST.__init__(self, start, end)
1940        self.template = template
1941        self.variables = list(variables)
1942
1943    def __repr__(self):
1944        return "%s(%r, %r, %r, %s)" % (self.__class__.__name__, self.start, self.end, self.template, repr(self.variables)[1:-1])
1945
1946    def compile(self, template):
1947        ra = template._allocreg()
1948        template.opcode("builddict", r1=ra)
1949        for item in self.variables:
1950            if len(item) == 1:
1951                rd = item[0].compile(template)
1952                template.opcode("updatedict", r1=ra, r2=rd)
1953                template._freereg(rd)
1954            else:
1955                (key, value) = item
1956                rv = value.compile(template)
1957                rk = template._allocreg()
1958                template.opcode("loadstr", r1=rk, arg=key.name)
1959                template.opcode("adddict", r1=ra, r2=rk, r3=rv)
1960                template._freereg(rk)
1961                template._freereg(rv)
1962        rt = self.template.compile(template)
1963        template.opcode("render", r1=rt, r2=ra)
1964        template._freereg(rt)
1965        template._freereg(ra)
1966
1967
1968###
1969### Tokenizer
1970###
1971
1972class Scanner(spark.Scanner):
1973    reflags = re.UNICODE
1974
1975    def tokenize(self, location):
1976        self.collectstr = []
1977        self.rv = []
1978        self.start = 0
1979        try:
1980            spark.Scanner.tokenize(self, location.code)
1981            if self.mode != "default":
1982                raise UnterminatedStringError()
1983        except Exception, exc:
1984            raise Error(location, exc)
1985        return self.rv
1986
1987    # Color tokens must be in the order of decreasing length
1988    @spark.token("\\#[0-9a-fA-F]{8}", "default")
1989    def color8(self, start, end, s):
1990        self.rv.append(Color(start, end, color.Color(int(s[1:3], 16), int(s[3:5], 16), int(s[5:7], 16), int(s[7:], 16))))
1991
1992    @spark.token("\\#[0-9a-fA-F]{6}", "default")
1993    def color6(self, start, end, s):
1994        self.rv.append(Color(start, end, color.Color(int(s[1:3], 16), int(s[3:5], 16), int(s[5:], 16))))
1995
1996    @spark.token("\\#[0-9a-fA-F]{4}", "default")
1997    def color4(self, start, end, s):
1998        self.rv.append(Color(start, end, color.Color(17*int(s[1], 16), 17*int(s[2], 16), 17*int(s[3], 16), 17*int(s[4], 16))))
1999
2000    @spark.token("\\#[0-9a-fA-F]{3}", "default")
2001    def color3(self, start, end, s):
2002        self.rv.append(Color(start, end, color.Color(17*int(s[1], 16), 17*int(s[2], 16), 17*int(s[3], 16))))
2003
2004    # Must be before the int and float constants
2005    @spark.token("\\d{4}-\\d{2}-\\d{2}T(\\d{2}:\\d{2}(:\\d{2}(\\.\\d{6})?)?)?", "default")
2006    def date(self, start, end, s):
2007        self.rv.append(Date(start, end, datetime.datetime(*map(int, filter(None, datesplitter.split(s))))))
2008
2009    @spark.token("\\(|\\)|\\[|\\]|\\{|\\}|\\.|,|==|\\!=|<=|<|>=|>|=|\\+=|\\-=|\\*=|//=|/=|%=|%|:|\\+|-|\\*\\*|\\*|//|/", "default")
2010    def token(self, start, end, s):
2011        self.rv.append(Token(start, end, s))
2012
2013    @spark.token("[a-zA-Z_][\\w]*", "default")
2014    def name(self, start, end, s):
2015        if s in ("in", "not", "or", "and", "del"):
2016            self.rv.append(Token(start, end, s))
2017        elif s == "None":
2018            self.rv.append(None_(start, end))
2019        elif s == "True":
2020            self.rv.append(True_(start, end))
2021        elif s == "False":
2022            self.rv.append(False_(start, end))
2023        else:
2024            self.rv.append(Name(start, end, s))
2025
2026    # We don't have negatve numbers, this is handled by constant folding in the AST for unary minus
2027    @spark.token("\\d+\\.\\d*([eE][+-]?\\d+)?", "default")
2028    @spark.token("\\d+(\\.\\d*)?[eE][+-]?\\d+", "default")
2029    def float(self, start, end, s):
2030        self.rv.append(Float(start, end, float(s)))
2031
2032    @spark.token("0[xX][\\da-fA-F]+", "default")
2033    def hexint(self, start, end, s):
2034        self.rv.append(Int(start, end, int(s[2:], 16)))
2035
2036    @spark.token("0[oO][0-7]+", "default")
2037    def octint(self, start, end, s):
2038        self.rv.append(Int(start, end, int(s[2:], 8)))
2039
2040    @spark.token("0[bB][01]+", "default")
2041    def binint(self, start, end, s):
2042        self.rv.append(Int(start, end, int(s[2:], 2)))
2043
2044    @spark.token("\\d+", "default")
2045    def int(self, start, end, s):
2046        self.rv.append(Int(start, end, int(s)))
2047
2048    @spark.token("'", "default")
2049    def beginstr1(self, start, end, s):
2050        self.mode = "str1"
2051        self.start = start
2052
2053    @spark.token('"', "default")
2054    def beginstr2(self, start, end, s):
2055        self.mode = "str2"
2056        self.start = start
2057
2058    @spark.token("'", "str1")
2059    @spark.token('"', "str2")
2060    def endstr(self, start, end, s):
2061        self.rv.append(Str(self.start, end, "".join(self.collectstr)))
2062        self.collectstr = []
2063        self.mode = "default"
2064
2065    @spark.token("\\s+", "default")
2066    def whitespace(self, start, end, s):
2067        pass
2068
2069    @spark.token("\\\\\\\\", "str1", "str2")
2070    def escapedbackslash(self, start, end, s):
2071        self.collectstr.append("\\")
2072
2073    @spark.token("\\\\'", "str1", "str2")
2074    def escapedapos(self, start, end, s):
2075        self.collectstr.append("'")
2076
2077    @spark.token('\\\\"', "str1", "str2")
2078    def escapedquot(self, start, end, s):
2079        self.collectstr.append('"')
2080
2081    @spark.token("\\\\a", "str1", "str2")
2082    def escapedbell(self, start, end, s):
2083        self.collectstr.append("\a")
2084
2085    @spark.token("\\\\b", "str1", "str2")
2086    def escapedbackspace(self, start, end, s):
2087        self.collectstr.append("\b")
2088
2089    @spark.token("\\\\f", "str1", "str2")
2090    def escapedformfeed(self, start, end, s):
2091        self.collectstr.append("\f")
2092
2093    @spark.token("\\\\n", "str1", "str2")
2094    def escapedlinefeed(self, start, end, s):
2095        self.collectstr.append("\n")
2096
2097    @spark.token("\\\\r", "str1", "str2")
2098    def escapedcarriagereturn(self, start, end, s):
2099        self.collectstr.append("\r")
2100
2101    @spark.token("\\\\t", "str1", "str2")
2102    def escapedtab(self, start, end, s):
2103        self.collectstr.append("\t")
2104
2105    @spark.token("\\\\v", "str1", "str2")
2106    def escapedverticaltab(self, start, end, s):
2107        self.collectstr.append("\v")
2108
2109    @spark.token("\\\\e", "str1", "str2")
2110    def escapedescape(self, start, end, s):
2111        self.collectstr.append("\x1b")
2112
2113    @spark.token("\\\\x[0-9a-fA-F]{2}", "str1", "str2")
2114    def escaped8bitchar(self, start, end, s):
2115        self.collectstr.append(unichr(int(s[2:], 16)))
2116
2117    @spark.token("\\\\u[0-9a-fA-F]{4}", "str1", "str2")
2118    def escaped16bitchar(self, start, end, s):
2119        self.collectstr.append(unichr(int(s[2:], 16)))
2120
2121    @spark.token(".|\\n", "str1", "str2")
2122    def text(self, start, end, s):
2123        self.collectstr.append(s)
2124
2125    @spark.token("(.|\\n)+", "default", "str1", "str2")
2126    def default(self, start, end, s):
2127        raise LexicalError(start, end, s)
2128
2129    def error(self, start, end, s):
2130        raise LexicalError(start, end, s)
2131
2132
2133###
2134### Parsers for different types of code
2135###
2136
2137class ExprParser(spark.Parser):
2138    emptyerror = "expression required"
2139    start = "expr0"
2140
2141    def __init__(self, scanner):
2142        spark.Parser.__init__(self)
2143        self.scanner = scanner
2144
2145    def compile(self, template):
2146        location = template.location
2147        if not location.code:
2148            raise ValueError(self.emptyerror)
2149        template.registers = set(xrange(10))
2150        try:
2151            ast = self.parse(self.scanner.tokenize(location))
2152            return ast.compile(template)
2153        except Exception, exc:
2154            raise Error(location, exc)
2155        finally:
2156            del template.registers
2157
2158    def typestring(self, token):
2159        return token.type
2160
2161    def error(self, token):
2162        raise SyntaxError(token)
2163
2164    def makeconst(self, start, end, value):
2165        if value is None:
2166            return None_(start, end)
2167        elif value is True:
2168            return True_(start, end)
2169        elif value is False:
2170            return False_(start, end)
2171        elif isinstance(value, int):
2172            return Int(start, end, value)
2173        elif isinstance(value, float):
2174            return Float(start, end, value)
2175        elif isinstance(value, basestring):
2176            return Str(start, end, value)
2177        elif isinstance(value, color.Color):
2178            return Color(start, end, value)
2179        else:
2180            raise TypeError("can't convert %r" % value)
2181
2182    # To implement operator precedence, each expression rule has the precedence in its name. The highest precedence is 11 for atomic expressions.
2183    # Each expression can have only expressions as parts which have the some or a higher precedence with two exceptions:
2184    #    1. Expressions where there's no ambiguity, like the index for a getitem/getslice or function/method arguments;
2185    #    2. Brackets, which can be used to boost the precedence of an expression to the level of an atomic expression.
2186
2187    @spark.production('expr11 ::= none')
2188    @spark.production('expr11 ::= true')
2189    @spark.production('expr11 ::= false')
2190    @spark.production('expr11 ::= str')
2191    @spark.production('expr11 ::= int')
2192    @spark.production('expr11 ::= float')
2193    @spark.production('expr11 ::= date')
2194    @spark.production('expr11 ::= color')
2195    @spark.production('expr11 ::= name')
2196    def expr_atom(self, atom):
2197        return atom
2198
2199    @spark.production('expr11 ::= [ ]')
2200    def expr_emptylist(self, _0, _1):
2201        return List(_0.start, _1.end)
2202
2203    @spark.production('buildlist ::= [ expr0')
2204    def expr_buildlist(self, _0, expr):
2205        return List(_0.start, expr.end, expr)
2206
2207    @spark.production('buildlist ::= buildlist , expr0')
2208    def expr_addlist(self, list, _0, expr):
2209        list.items.append(expr)
2210        list.end = expr.end
2211        return list
2212
2213    @spark.production('expr11 ::= buildlist ]')
2214    def expr_finishlist(self, list, _0):
2215        list.end = _0.end
2216        return list
2217
2218    @spark.production('expr11 ::= buildlist , ]')
2219    def expr_finishlist1(self, list, _0, _1):
2220        list.end = _1.end
2221        return list
2222
2223    @spark.production('expr11 ::= { }')
2224    def expr_emptydict(self, _0, _1):
2225        return Dict(_0.start, _1.end)
2226
2227    @spark.production('builddict ::= { expr0 : expr0')
2228    def expr_builddict(self, _0, exprkey, _1, exprvalue):
2229        return Dict(_0.start, exprvalue.end, (exprkey, exprvalue))
2230
2231    @spark.production('builddict ::= { ** expr0')
2232    def expr_builddictupdate(self, _0, _1, expr):
2233        return Dict(_0.start, expr.end, (expr,))
2234
2235    @spark.production('builddict ::= builddict , expr0 : expr0')
2236    def expr_adddict(self, dict, _0, exprkey, _1, exprvalue):
2237        dict.items.append((exprkey, exprvalue))
2238        dict.end = exprvalue.end
2239        return dict
2240
2241    @spark.production('builddict ::= builddict , ** expr0')
2242    def expr_updatedict(self, dict, _0, _1, expr):
2243        dict.items.append((expr,))
2244        dict.end = expr.end
2245        return dict
2246
2247    @spark.production('expr11 ::= builddict }')
2248    def expr_finishdict(self, dict, _0):
2249        dict.end = _0.end
2250        return dict
2251
2252    @spark.production('expr11 ::= builddict , }')
2253    def expr_finishdict1(self, dict, _0, _1):
2254        dict.end = _1.end
2255        return dict
2256
2257    @spark.production('expr11 ::= ( expr0 )')
2258    def expr_bracket(self, _0, expr, _1):
2259        return expr
2260
2261    @spark.production('expr10 ::= name ( )')
2262    def expr_callfunc0(self, name, _0, _1):
2263        return CallFunc(name.start, _1.end, name, [])
2264
2265    @spark.production('expr10 ::= name ( expr0 )')
2266    def expr_callfunc1(self, name, _0, arg0, _1):
2267        return CallFunc(name.start, _1.end, name, [arg0])
2268
2269    @spark.production('expr10 ::= name ( expr0 , expr0 )')
2270    def expr_callfunc2(self, name, _0, arg0, _1, arg1, _2):
2271        return CallFunc(name.start, _2.end, name, [arg0, arg1])
2272
2273    @spark.production('expr10 ::= name ( expr0 , expr0 , expr0 )')
2274    def expr_callfunc3(self, name, _0, arg0, _1, arg1, _2, arg2, _3):
2275        return CallFunc(name.start, _3.end, name, [arg0, arg1, arg2])
2276
2277    @spark.production('expr10 ::= name ( expr0 , expr0 , expr0 , expr0 )')
2278    def expr_callfunc4(self, name, _0, arg0, _1, arg1, _2, arg2, _3, arg3, _4):
2279        return CallFunc(name.start, _4.end, name, [arg0, arg1, arg2, arg3])
2280
2281    @spark.production('expr9 ::= expr9 . name')
2282    def expr_getattr(self, expr, _0, name):
2283        return GetAttr(expr.start, name.end, expr, name)
2284
2285    @spark.production('expr9 ::= expr9 . name ( )')
2286    def expr_callmeth0(self, expr, _0, name, _1, _2):
2287        return CallMeth(expr.start, _2.end, name, expr, [])
2288
2289    @spark.production('expr9 ::= expr9 . name ( expr0 )')
2290    def expr_callmeth1(self, expr, _0, name, _1, arg1, _2):
2291        return CallMeth(expr.start, _2.end, name, expr, [arg1])
2292
2293    @spark.production('expr9 ::= expr9 . name ( expr0 , expr0 )')
2294    def expr_callmeth2(self, expr, _0, name, _1, arg1, _2, arg2, _3):
2295        return CallMeth(expr.start, _3.end, name, expr, [arg1, arg2])
2296
2297    @spark.production('expr9 ::= expr9 . name ( expr0 , expr0 , expr0 )')
2298    def expr_callmeth3(self, expr, _0, name, _1, arg1, _2, arg2, _3, arg3, _4):
2299        return CallMeth(expr.start, _4.end, name, expr, [arg1, arg2, arg3])
2300
2301    @spark.production('callmethkw ::= expr9 . name ( name = expr0')
2302    def methkw_startname(self, expr, _0, methname, _1, argname, _2, argvalue):
2303        return CallMethKeywords(expr.start, argvalue.end, methname, expr, [(argname, argvalue)])
2304
2305    @spark.production('callmethkw ::= expr9 . name ( ** expr0')
2306    def methkw_startdict(self, expr, _0, methname, _1, _2, argvalue):
2307        return CallMethKeywords(expr.start, argvalue.end, methname, expr, [(argvalue,)])
2308
2309    @spark.production('callmethkw ::= callmethkw , name = expr0')
2310    def methkw_buildname(self, call, _0, argname, _1, argvalue):
2311        call.args.append((argname, argvalue))
2312        call.end = argvalue.end
2313        return call
2314
2315    @spark.production('callmethkw ::= callmethkw , ** expr0')
2316    def methkw_builddict(self, call, _0, _1, argvalue):
2317        call.args.append((argvalue,))
2318        call.end = argvalue.end
2319        return call
2320
2321    @spark.production('expr9 ::= callmethkw )')
2322    def methkw_finish(self, call, _0):
2323        call.end = _0.end
2324        return call
2325
2326    @spark.production('expr9 ::= expr9 [ expr0 ]')
2327    def expr_getitem(self, expr, _0, key, _1):
2328        if isinstance(expr, Const) and isinstance(key, Const): # Constant folding
2329            return self.makeconst(expr.start, _1.end, expr.value[key.value])
2330        return GetItem(expr.start, _1.end, expr, key)
2331
2332    @spark.production('expr8 ::= expr8 [ expr0 : expr0 ]')
2333    def expr_getslice12(self, expr, _0, index1, _1, index2, _2):
2334        if isinstance(expr, Const) and isinstance(index1, Const) and isinstance(index2, Const): # Constant folding
2335            return self.makeconst(expr.start, _2.end, expr.value[index1.value:index2.value])
2336        return GetSlice12(expr.start, _2.end, expr, index1, index2)
2337
2338    @spark.production('expr8 ::= expr8 [ expr0 : ]')
2339    def expr_getslice1(self, expr, _0, index1, _1, _2):
2340        if isinstance(expr, Const) and isinstance(index1, Const): # Constant folding
2341            return self.makeconst(expr.start, _2.end, expr.value[index1.value:])
2342        return GetSlice1(expr.start, _2.end, expr, index1)
2343
2344    @spark.production('expr8 ::= expr8 [ : expr0 ]')
2345    def expr_getslice2(self, expr, _0, _1, index2, _2):
2346        if isinstance(expr, Const) and isinstance(index2, Const): # Constant folding
2347            return self.makeconst(expr.start, _2.end, expr.value[:index2.value])
2348        return GetSlice2(expr.start, _2.end, expr, index2)
2349
2350    @spark.production('expr7 ::= - expr7')
2351    def expr_neg(self, _0, expr):
2352        if isinstance(expr, Const): # Constant folding
2353            return self.makeconst(_0.start, expr.end, -expr.value)
2354        return Neg(_0.start, expr.end, expr)
2355
2356    @spark.production('expr6 ::= expr6 * expr6')
2357    def expr_mul(self, obj1, _0, obj2):
2358        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2359            return self.makeconst(obj1.start, obj2.end, obj1.value * obj2.value)
2360        return Mul(obj1.start, obj2.end, obj1, obj2)
2361
2362    @spark.production('expr6 ::= expr6 // expr6')
2363    def expr_floordiv(self, obj1, _0, obj2):
2364        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2365            return self.makeconst(obj1.start, obj2.end, obj1.value // obj2.value)
2366        return FloorDiv(obj1.start, obj2.end, obj1, obj2)
2367
2368    @spark.production('expr6 ::= expr6 / expr6')
2369    def expr_truediv(self, obj1, _0, obj2):
2370        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2371            return self.makeconst(obj1.start, obj2.end, obj1.value / obj2.value)
2372        return TrueDiv(obj1.start, obj2.end, obj1, obj2)
2373
2374    @spark.production('expr6 ::= expr6 % expr6')
2375    def expr_mod(self, obj1, _0, obj2):
2376        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2377            return self.makeconst(obj1.start, obj2.end, obj1.value % obj2.value)
2378        return Mod(obj1.start, obj2.end, obj1, obj2)
2379
2380    @spark.production('expr5 ::= expr5 + expr5')
2381    def expr_add(self, obj1, _0, obj2):
2382        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2383            return self.makeconst(obj1.start, obj2.end, obj1.value + obj2.value)
2384        return Add(obj1.start, obj2.end, obj1, obj2)
2385
2386    @spark.production('expr5 ::= expr5 - expr5')
2387    def expr_sub(self, obj1, _0, obj2):
2388        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2389            return self.makeconst(obj1.start, obj2.end, obj1.value - obj2.value)
2390        return Sub(obj1.start, obj2.end, obj1, obj2)
2391
2392    @spark.production('expr4 ::= expr4 == expr4')
2393    def expr_eq(self, obj1, _0, obj2):
2394        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2395            return self.makeconst(obj1.start, obj2.end, obj1.value == obj2.value)
2396        return EQ(obj1.start, obj2.end, obj1, obj2)
2397
2398    @spark.production('expr4 ::= expr4 != expr4')
2399    def expr_ne(self, obj1, _0, obj2):
2400        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2401            return self.makeconst(obj1.start, obj2.end, obj1.value != obj2.value)
2402        return NE(obj1.start, obj2.end, obj1, obj2)
2403
2404    @spark.production('expr4 ::= expr4 < expr4')
2405    def expr_lt(self, obj1, _0, obj2):
2406        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2407            return self.makeconst(obj1.start, obj2.end, obj1.value < obj2.value)
2408        return LT(obj1.start, obj2.end, obj1, obj2)
2409
2410    @spark.production('expr4 ::= expr4 <= expr4')
2411    def expr_le(self, obj1, _0, obj2):
2412        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2413            return self.makeconst(obj1.start, obj2.end, obj1.value <= obj2.value)
2414        return LE(obj1.start, obj2.end, obj1, obj2)
2415
2416    @spark.production('expr4 ::= expr4 > expr4')
2417    def expr_gt(self, obj1, _0, obj2):
2418        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2419            return self.makeconst(obj1.start, obj2.end, obj1.value > obj2.value)
2420        return GT(obj1.start, obj2.end, obj1, obj2)
2421
2422    @spark.production('expr4 ::= expr4 >= expr4')
2423    def expr_ge(self, obj1, _0, obj2):
2424        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2425            return self.makeconst(obj1.start, obj2.end, obj1.value >= obj2.value)
2426        return GE(obj1.start, obj2.end, obj1, obj2)
2427
2428    @spark.production('expr3 ::= expr3 in expr3')
2429    def expr_contains(self, obj, _0, container):
2430        if isinstance(obj, Const) and isinstance(container, Const): # Constant folding
2431            return self.makeconst(obj.start, container.end, obj.value in container.value)
2432        return Contains(obj.start, container.end, obj, container)
2433
2434    @spark.production('expr3 ::= expr3 not in expr3')
2435    def expr_notcontains(self, obj, _0, _1, container):
2436        if isinstance(obj, Const) and isinstance(container, Const): # Constant folding
2437            return self.makeconst(obj.start, container.end, obj.value not in container.value)
2438        return NotContains(obj.start, container.end, obj, container)
2439
2440    @spark.production('expr2 ::= not expr2')
2441    def expr_not(self, _0, expr):
2442        if isinstance(expr, Const): # Constant folding
2443            return self.makeconst(_0.start, expr.end, not expr.value)
2444        return Not(_0.start, expr.end, expr)
2445
2446    @spark.production('expr1 ::= expr1 and expr1')
2447    def expr_and(self, obj1, _0, obj2):
2448        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2449            return self.makeconst(obj1.start, obj2.end, bool(obj1.value and obj2.value))
2450        return And(obj1.start, obj2.end, obj1, obj2)
2451
2452    @spark.production('expr0 ::= expr0 or expr0')
2453    def expr_or(self, obj1, _0, obj2):
2454        if isinstance(obj1, Const) and isinstance(obj2, Const): # Constant folding
2455            return self.makeconst(obj1.start, obj2.end, bool(obj1.value or obj2.value))
2456        return Or(obj1.start, obj2.end, obj1, obj2)
2457
2458    # These rules make operators of different precedences interoperable, by allowing an expression to "drop" its precedence.
2459    @spark.production('expr10 ::= expr11')
2460    @spark.production('expr9 ::= expr10')
2461    @spark.production('expr8 ::= expr9')
2462    @spark.production('expr7 ::= expr8')
2463    @spark.production('expr6 ::= expr7')
2464    @spark.production('expr5 ::= expr6')
2465    @spark.production('expr4 ::= expr5')
2466    @spark.production('expr3 ::= expr4')
2467    @spark.production('expr2 ::= expr3')
2468    @spark.production('expr1 ::= expr2')
2469    @spark.production('expr0 ::= expr1')
2470    def expr_dropprecedence(self, expr):
2471        return expr
2472
2473
2474class ForParser(ExprParser):
2475    emptyerror = "loop expression required"
2476    start = "for"
2477   
2478    @spark.production('for ::= name in expr0')
2479    def for0(self, iter, _0, cont):
2480        return For(iter.start, cont.end, iter, cont)
2481
2482    @spark.production('for ::= ( name , ) in expr0')
2483    def for1(self, _0, iter, _1, _2, _3, cont):
2484        return For(_0.start, cont.end, [iter], cont)
2485
2486    @spark.production('for ::= ( name , name ) in expr0')
2487    def for2a(self, _0, iter1, _1, iter2, _2, _3, cont):
2488        return For(_0.start, cont.end, [iter1, iter2], cont)
2489
2490    @spark.production('for ::= ( name , name , ) in expr0')
2491    def for2b(self, _0, iter1, _1, iter2, _2, _3, _4, cont):
2492        return For(_0.start, cont.end, [iter1, iter2], cont)
2493
2494    @spark.production('for ::= ( name , name , name ) in expr0')
2495    def for3a(self, _0, iter1, _1, iter2, _2, iter3, _3, _4, cont):
2496        return For(_0.start, cont.end, [iter1, iter2, iter3], cont)
2497
2498    @spark.production('for ::= ( name , name , name , ) in expr0')
2499    def for3b(self, _0, iter1, _1, iter2, _2, iter3, _3, _4, _5, cont):
2500        return For(_0.start, cont.end, [iter1, iter2, iter3], cont)
2501
2502
2503class StmtParser(ExprParser):
2504    emptyerror = "statement required"
2505    start = "stmt"
2506
2507    @spark.production('stmt ::= name = expr0')
2508    def stmt_assign(self, name, _0, value):
2509        return StoreVar(name.start, value.end, name, value)
2510
2511    @spark.production('stmt ::= name += expr0')
2512    def stmt_iadd(self, name, _0, value):
2513        return AddVar(name.start, value.end, name, value)
2514
2515    @spark.production('stmt ::= name -= expr0')
2516    def stmt_isub(self, name, _0, value):
2517        return SubVar(name.start, value.end, name, value)
2518
2519    @spark.production('stmt ::= name *= expr0')
2520    def stmt_imul(self, name, _0, value):
2521        return MulVar(name.start, value.end, name, value)
2522
2523    @spark.production('stmt ::= name /= expr0')
2524    def stmt_itruediv(self, name, _0, value):
2525        return TrueDivVar(name.start, value.end, name, value)
2526
2527    @spark.production('stmt ::= name //= expr0')
2528    def stmt_ifloordiv(self, name, _0, value):
2529        return FloorDivVar(name.start, value.end, name, value)
2530
2531    @spark.production('stmt ::= name %= expr0')
2532    def stmt_imod(self, name, _0, value):
2533        return ModVar(name.start, value.end, name, value)
2534
2535    @spark.production('stmt ::= del name')
2536    def stmt_del(self, _0, name):
2537        return DelVar(_0.start, name.end, name)
2538
2539
2540class RenderParser(ExprParser):
2541    emptyerror = "render statement required"
2542    start = "render"
2543
2544    @spark.production('render ::= expr0 ( )')
2545    def emptyrender(self, template, _0, _1):
2546        return Render(template.start, _1.end, template)
2547
2548    @spark.production('buildrender ::= expr0 ( name = expr0')
2549    def startrender(self, template, _0, argname, _1, argvalue):
2550        return Render(template.start, argvalue.end, template, (argname, argvalue))
2551
2552    @spark.production('buildrender ::= expr0 ( ** expr0')
2553    def startrenderupdate(self, template, _0, _1, arg):
2554        return Render(template.start, arg.end, template, (arg, ))
2555
2556    @spark.production('buildrender ::= buildrender , name = expr0')
2557    def buildrender(self, render, _0, argname, _1, argvalue):
2558        render.variables.append((argname, argvalue))
2559        render.end = argvalue.end
2560        return render
2561
2562    @spark.production('buildrender ::= buildrender , ** expr0')
2563    def buildrenderupdate(self, render, _0, _1, arg):
2564        render.variables.append((arg,))
2565        render.end = arg.end
2566        return render
2567
2568    @spark.production('render ::= buildrender )')
2569    def finishrender(self, render, _0):
2570        render.end = _0.end
2571        return render
2572
2573    @spark.production('render ::= buildrender , )')
2574    def finishrender1(self, render, _0, _1):
2575        render.end = _1.end
2576        return render
2577
2578
2579###
2580### Helper functions used at template runtime
2581###
2582
2583def _oct(value):
2584    """
2585    Helper for the ``oct`` function.
2586    """
2587    if value == 0:
2588        return "0o0"
2589    elif value < 0:
2590        return "-0o" + oct(value)[2:]
2591    else:
2592        return "0o" + oct(value)[1:]
2593
2594
2595def _bin(value):
2596    """
2597    Helper for the ``bin`` function.
2598    """
2599    if value == 0:
2600        return "0b0"
2601    if value < 0:
2602        value = -value
2603        prefix = "-0b"
2604    else:
2605        prefix = "0b"
2606    v = []
2607    while value:
2608        v.append(str(value&1))
2609        value >>= 1
2610    return prefix+"".join(v)[::-1]
2611
2612
2613def _format(obj, format):
2614    """
2615    Helper for the ``format`` method.
2616    """
2617    if isinstance(obj, datetime.datetime):
2618        if "%f" in format:
2619            format = format.replace("%f", "%06d" % obj.microsecond) # FIXME: This would replace "%%f", which is wrong (wait for Python 2.6)
2620        return obj.strftime(format.encode("utf-8")) # FIXME: We shouldn't have to encode the format string (wait for Python 3.0)
2621    elif obj is None or isinstance(obj, (int, long, float, str, unicode)):
2622        from ll import stringformat
2623        return stringformat.format_builtin_type(obj, format)
2624    else:
2625        return obj.format(format) # This will raise an ``AttributeError``
2626
2627
2628def _repr(obj):
2629    """
2630    Helper for the ``repr`` function.
2631    """
2632    if isinstance(obj, unicode):
2633        return unicode(repr(obj)[1:])
2634    elif isinstance(obj, str):
2635        return unicode(repr(obj))
2636    elif isinstance(obj, datetime.datetime):
2637        return unicode(obj.isoformat())
2638    elif isinstance(obj, color.Color):
2639        if obj[3] == 0xff:
2640            s = "#%02x%02x%02x" % (obj[0], obj[1], obj[2])
2641            if s[1]==s[2] and s[3]==s[4] and s[5]==s[6]:
2642                return "#%s%s%s" % (s[1], s[3], s[5])
2643            return s
2644        else:
2645            s = "#%02x%02x%02x%02x" % obj
2646            if s[1]==s[2] and s[3]==s[4] and s[5]==s[6] and s[7]==s[8]:
2647                return "#%s%s%s%s" % (s[1], s[3], s[5], s[7])
2648            return s
2649    elif isinstance(obj, list):
2650        return u"[%s]" % u", ".join(_repr(item) for item in obj)
2651    elif isinstance(obj, dict):
2652        return u"{%s}" % u", ".join(u"%s: %s" % (_repr(key), _repr(value)) for (key, value) in obj.iteritems())
2653    else:
2654        return unicode(repr(obj))
2655
2656
2657def _csv(obj):
2658    """
2659    Helper for the ``csv`` function.
2660    """
2661    if obj is None:
2662        return u""
2663    elif not isinstance(obj, basestring):
2664        obj = _repr(obj)
2665    if any(c in obj for c in ',"\n'):
2666        return u'"%s"' % obj.replace('"', '""')
2667    return obj
2668
2669
2670def _type(obj):
2671    """
2672    Helper for the ``type`` function.
2673    """
2674    if obj is None:
2675        return u"none"
2676    elif isinstance(obj, basestring):
2677        return u"str"
2678    elif isinstance(obj, bool):
2679        return u"bool"
2680    elif isinstance(obj, (int, long)):
2681        return u"int"
2682    elif isinstance(obj, float):
2683        return u"float"
2684    elif isinstance(obj, (datetime.datetime, datetime.date)):
2685        return u"date"
2686    elif isinstance(obj, color.Color):
2687        return u"color"
2688    elif isinstance(obj, (list, tuple)):
2689        return u"list"
2690    elif isinstance(obj, dict):
2691        return u"dict"
2692    elif hasattr(obj, "__call__"):
2693        return u"template"
2694    return None
Note: See TracBrowser for help on using the browser.