root/livinglogic.python.xist/src/ll/ul4c.py @ 3776:a4f2a72b483e

Revision 3776:a4f2a72b483e, 88.8 KB (checked in by Walter Doerwald <walter@…>, 10 years ago)

Add UL4 functions float() and iscolor().

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