root/livinglogic.python.xist/src/ll/ul4c.py @ 5308:c5ea99468f5e

Revision 5308:c5ea99468f5e, 95.8 KB (checked in by Walter Doerwald <walter@…>, 7 years ago)

type() must return "template" for a TemplateClosure? object too.

Line 
1# -*- coding: utf-8 -*-
2
3## Copyright 2009-2013 by LivingLogic AG, Bayreuth/Germany
4## Copyright 2009-2013 by Walter Dörwald
5##
6## All Rights Reserved
7##
8## See ll/xist/__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 an internal format, which makes it
18possible to implement template renderers in multiple programming languages.
19"""
20
21
22__docformat__ = "reStructuredText"
23
24
25import re, datetime, urllib.parse as urlparse, json, collections, locale, itertools, random, datetime, unicodedata
26
27import antlr3
28
29from ll import color, misc
30
31
32# Regular expression used for splitting dates in isoformat
33datesplitter = re.compile("[-T:.]")
34
35
36def register(name):
37    from ll import ul4on
38    def registration(cls):
39        ul4on.register("de.livinglogic.ul4." + name)(cls)
40        cls.type = name
41        return cls
42    return registration
43
44
45class Object:
46    fields = {}
47
48    def __getitem__(self, key):
49        if key in self.fields:
50            return getattr(self, key)
51        raise KeyError(key)
52
53
54###
55### Location information
56###
57
58@register("location")
59class Location(Object):
60    """
61    A :class:`Location` object contains information about the location of a
62    template tag.
63    """
64    __slots__ = ("source", "type", "starttag", "endtag", "startcode", "endcode")
65    fields = {"source", "type", "starttag", "endtag", "startcode", "endcode", "tag", "code"}
66
67    def __init__(self, source=None, type=None, starttag=None, endtag=None, startcode=None, endcode=None):
68        """
69        Create a new :class:`Location` object. The arguments have the following
70        meaning:
71
72            :var:`source`
73                The complete source string
74
75            :var:`type`
76                The tag type (i.e. ``"for"``, ``"if"``, etc. or ``None`` for
77                literal text)
78
79            :var:`starttag`
80                The start position of the start delimiter.
81
82            :var:`endtag`
83                The end position of the end delimiter.
84
85            :var:`startcode`
86                The start position of the tag code.
87
88            :var:`endcode`
89                The end position of the tag code.
90        """
91        self.source = source
92        self.type = type
93        self.starttag = starttag
94        self.endtag = endtag
95        self.startcode = startcode
96        self.endcode = endcode
97
98    @property
99    def code(self):
100        return self.source[self.startcode:self.endcode]
101
102    @property
103    def tag(self):
104        return self.source[self.starttag:self.endtag]
105
106    def __repr__(self):
107        return "<{}.{} {} at {:#x}>".format(self.__class__.__module__, self.__class__.__name__, self, id(self))
108
109    def pos(self):
110        lastlinefeed = self.source.rfind("\n", 0, self.starttag)
111        if lastlinefeed >= 0:
112            return (self.source.count("\n", 0, self.starttag)+1, self.starttag-lastlinefeed)
113        else:
114            return (1, self.starttag + 1)
115
116    def __str__(self):
117        (line, col) = self.pos()
118        return "{!r} at {}:{} (line {}, col {})".format(self.tag, self.starttag, self.endtag, line, col)
119
120    def ul4ondump(self, encoder):
121        encoder.dump(self.source)
122        encoder.dump(self.type)
123        encoder.dump(self.starttag)
124        encoder.dump(self.endtag)
125        encoder.dump(self.startcode)
126        encoder.dump(self.endcode)
127
128    def ul4onload(self, decoder):
129        self.source = decoder.load()
130        self.type = decoder.load()
131        self.starttag = decoder.load()
132        self.endtag = decoder.load()
133        self.startcode = decoder.load()
134        self.endcode = decoder.load()
135
136
137###
138### Exceptions
139###
140
141class Error(Exception):
142    """
143    Exception class that wraps another exception and provides a location.
144    """
145    def __init__(self, location):
146        self.location = location
147
148    def __repr__(self):
149        return "<{}.{} in {} at {:#x}>".format(self.__class__.__module__, self.__class__.__name__, self.location, id(self))
150
151    def __str__(self):
152        return "in {}".format(self.location)
153
154
155class BlockError(Exception):
156    """
157    Exception that is raised by the compiler when an illegal block structure is
158    detected (e.g. an ``endif`` without a previous ``if``).
159    """
160
161    def __init__(self, message):
162        self.message = message
163
164    def __str__(self):
165        return self.message
166
167
168class UnknownFunctionError(Exception):
169    """
170    Exception that is raised by the renderer if the function to be executed is unknown.
171    """
172
173    def __init__(self, funcname):
174        self.funcname = funcname
175
176    def __str__(self):
177        return "function {!r} unknown".format(self.funcname)
178
179
180class UnknownMethodError(Exception):
181    """
182    Exception that is raised by the renderer if the method to be executed is unknown.
183    """
184
185    def __init__(self, methname):
186        self.methname = methname
187
188    def __str__(self):
189        return "method {!r} unknown".format(self.methname)
190
191
192###
193### Various versions of undefined objects
194###
195
196class Undefined(object):
197    def __bool__(self):
198        return False
199
200    def __iter__(self):
201        raise TypeError("{!r} doesn't support iteration".format(self))
202
203    def __len__(self):
204        raise AttributeError("{!r} has no len()".format(self))
205
206    def __getattr__(self, key):
207        raise AttributeError("{!r} has no attribute {!r}".format(self, key))
208
209    def __getitem__(self, key):
210        raise TypeError("{!r} doesn't support indexing (key={!r})".format(self, key))
211
212
213class UndefinedKey(Undefined):
214    def __init__(self, key):
215        self.__key = key
216
217    def __repr__(self):
218        return "undefined object for key {!r}".format(self.__key)
219
220
221class UndefinedIndex(Undefined):
222    def __init__(self, index):
223        self.__index = index
224
225    def __repr__(self):
226        return "undefined object at index {!r}".format(self.__index)
227
228
229###
230### Compiler stuff: Tokens and nodes for the AST
231###
232
233class AST(Object):
234    """
235    Base class for all syntax tree nodes.
236    """
237    # used in :meth:`format` to decide if we need brackets around an operator
238    precedence = None
239    associative = True
240
241    # Set of attributes available via :meth:`getitem`.
242    fields = {"type"}
243
244    def __getitem__(self, key):
245        if key in self.fields:
246            return getattr(self, key)
247        raise KeyError(key)
248
249    def __repr__(self):
250        return "<{0.__class__.__module__}.{0.__class__.__qualname__} at {1:#x}>".format(self, id(self))
251
252    def _repr_pretty_(self, p, cycle):
253        p.text(repr(self))
254
255    def iternodes(self):
256        yield self
257
258    def _formatop(self, op):
259        if op.precedence < self.precedence:
260            return "({})".format(op.format(0, True))
261        elif op.precedence == self.precedence and (not isinstance(op, self.__class__) or not self.associative):
262            return "({})".format(op.format(0, True))
263        else:
264            return op.format(0, True)
265
266    @misc.notimplemented
267    def format(self, indent, keepws):
268        """
269        Format :var:`self` (with the indentation level :var:`indent`).
270
271        This is used by :meth:`__str__.
272        """
273
274    @misc.notimplemented
275    def formatpython(self, indent, keepws):
276        """
277        Format :var:`self` into valid Python source code.
278        """
279
280
281class Tag(AST):
282    """
283    Base class for all syntax tree nodes that are the top level node in a
284    template tag.
285    """
286    # Set of attributes available via :meth:`getitem`.
287    fields = AST.fields.union({"location"})
288
289    def __init__(self, location=None):
290        self.location = location
291
292    def ul4ondump(self, encoder):
293        encoder.dump(self.location)
294
295    def ul4onload(self, decoder):
296        self.location = decoder.load()
297
298
299@register("text")
300class Text(Tag):
301    """
302    AST node for literal text.
303    """
304
305    def text(self, keepws):
306        # If ``keepws`` is true, we output the literal text from the location info.
307        # Otherwise we have to strip linefeeds and indentation
308        text = self.location.code
309        if not keepws:
310            text = "".join(line.lstrip() for line in text.splitlines())
311        return text
312
313    def __repr__(self):
314        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.location.code!r} at {1:#x}>".format(self, id(self))
315
316    def format(self, indent, keepws):
317        text = self.text(keepws)
318        if text:
319            return "{}text {!r}\n".format(indent*"\t", self.text(keepws))
320        else:
321            return ""
322
323    def formatpython(self, indent, keepws):
324        text = self.text(keepws)
325        if text:
326            return "{i}# literal at position {l.starttag}:{l.endtag} ({id})\n{i}yield {t!r}\n".format(i=indent*"\t", id=id(self), l=self.location, t=text)
327        else:
328            return "{i}# ignored literal at position {l.starttag}:{l.endtag} ({id})\n\n".format(i=indent*"\t", id=id(self), l=self.location)
329
330
331@register("const")
332class Const(AST):
333    """
334    Load a constant
335    """
336    precedence = 11
337    fields = AST.fields.union({"value"})
338
339    def __init__(self, value=None):
340        self.value = value
341
342    def format(self, indent, keepws):
343        return _repr(self.value)
344
345    def formatpython(self, indent, keepws):
346        if isinstance(self.value, color.Color):
347            return "color.{!r}".format(self.value)
348        return repr(self.value)
349
350    def ul4ondump(self, encoder):
351        encoder.dump(self.value)
352
353    def ul4onload(self, decoder):
354        self.value = decoder.load()
355
356    def __repr__(self):
357        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.value!r} at {1:#x}>".format(self, id(self))
358
359
360@register("list")
361class List(AST):
362    """
363    AST nodes for loading a list object.
364    """
365
366    precedence = 11
367    fields = AST.fields.union({"items"})
368
369    def __init__(self, *items):
370        self.items = list(items)
371
372    def iternodes(self):
373        yield self
374        for item in self.items:
375            yield from item.iternodes()
376
377    def __repr__(self):
378        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.items!r} at {1:#x}>".format(self, id(self))
379
380    def _repr_pretty_(self, p, cycle):
381        if cycle:
382            p.text("{0.__class__.__module__}.{0.__class__.__qualname__}(...)".format(self))
383        else:
384            with p.group(4, "{0.__class__.__module__}.{0.__class__.__qualname__}(".format(self), ")"):
385                for (i, item) in enumerate(self.items):
386                    if i:
387                        p.text(",")
388                        p.breakable()
389                    else:
390                        p.breakable("")
391                    p.pretty(item)
392                p.breakable()
393                p.text("at {:#x}".format(id(self)))
394
395    def format(self, indent, keepws):
396        return "[{}]".format(", ".join(item.format(indent, keepws) for item in self.items))
397
398    def formatpython(self, indent, keepws):
399        return "[{}]".format(", ".join(item.formatpython(indent, keepws) for item in self.items))
400
401    def ul4ondump(self, encoder):
402        encoder.dump(self.items)
403
404    def ul4onload(self, decoder):
405        self.items = decoder.load()
406
407
408@register("listcomp")
409class ListComp(AST):
410    """
411    AST node for list comprehension.
412    """
413
414    precedence = 11
415    fields = AST.fields.union({"item", "varname", "container", "condition"})
416
417    def __init__(self, item=None, varname=None, container=None, condition=None):
418        super().__init__()
419        self.item = item
420        self.varname = varname
421        self.container = container
422        self.condition = condition
423
424    def __repr__(self):
425        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} item={0.item!r} varname={0.varname!r} container={0.container!r}".format(self)
426        if self.condition is not None:
427            s += " condition={0.condition!r}".format(self)
428        return s + " at {:#x}>".format(id(self))
429
430    def _repr_pretty_(self, p, cycle):
431        if cycle:
432            p.text("{0.__class__.__module__}.{0.__class__.__qualname__}(...)".format(self))
433        else:
434            with p.group(4, "{0.__class__.__module__}.{0.__class__.__qualname__}(".format(self), ")"):
435                p.breakable("")
436                p.text("item=")
437                p.pretty(self.item)
438                p.text(",")
439                p.breakable()
440                p.text("varname=")
441                p.pretty(self.varname)
442                p.text(",")
443                p.breakable()
444                p.text("container=")
445                p.pretty(self.container)
446                if self.condition is not None:
447                    p.text(",")
448                    p.breakable()
449                    p.text("condition=")
450                    p.pretty(self.condition)
451                p.breakable()
452                p.text("at {:#x}".format(id(self)))
453
454    def iternodes(self):
455        yield self
456        yield from self.item.iternodes()
457        yield from self.container.iternodes()
458        if self.condition is not None:
459            yield from self.condition.iternodes()
460
461    def format(self, indent, keepws):
462        s = "[{} for {} in".format(self.item.format(indent, keepws), _formatnestednameul4(self.varname), self.container.format(indent, keepws))
463        if self.condition is not None:
464            s += " if {}".format(self.condition.format(indent, keepws))
465        s += "]"
466        return s
467
468    def formatpython(self, indent, keepws):
469        s = "[{} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws))
470        if self.condition is not None:
471            s += " if {}".format(self.condition.formatpython(indent, keepws))
472        s += "]"
473        return s
474
475    def ul4ondump(self, encoder):
476        encoder.dump(self.item)
477        encoder.dump(self.varname)
478        encoder.dump(self.container)
479        encoder.dump(self.condition)
480
481    def ul4onload(self, decoder):
482        self.item = decoder.load()
483        self.varname = decoder.load()
484        self.container = decoder.load()
485        self.condition = decoder.load()
486
487
488@register("dict")
489class Dict(AST):
490    """
491    AST node for loading a dict object.
492    """
493
494    precedence = 11
495    fields = AST.fields.union({"items"})
496
497    def __init__(self, *items):
498        self.items = list(items)
499
500    def __repr__(self):
501        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.items!r} at {1:#x}>".format(self, id(self))
502
503    def _repr_pretty_(self, p, cycle):
504        if cycle:
505            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
506        else:
507            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
508                for item in self.items:
509                    p.breakable()
510                    if len(item) == 2:
511                        p.pretty(item[0])
512                        p.text("=")
513                        p.pretty(item[1])
514                    else:
515                        p.text("**")
516                        p.pretty(item)
517                p.breakable()
518                p.text("at {:#x}".format(id(self)))
519
520    def iternodes(self):
521        yield self
522        for item in self.items:
523            for subitem in item:
524                yield from subitem.iternodes()
525
526    def format(self, indent, keepws):
527        v = []
528        for item in self.items:
529            if len(item) == 2:
530                v.append("{}: {}".format(item[0], item[1].format(indent, keepws)))
531            else:
532                v.append("**{}".format(item[0].format(indent, keepws)))
533        return "{{{}}}".format(", ".join(v))
534
535    def formatpython(self, indent, keepws):
536        v = []
537        for item in self.items:
538            if len(item) == 1:
539                v.append("({},)".format(item[0].formatpython(indent, keepws)))
540            else:
541                v.append("({}, {})".format(item[0].formatpython(indent, keepws), item[1].formatpython(indent, keepws)))
542        return "ul4c._makedict({})".format(", ".join(v))
543
544    def ul4ondump(self, encoder):
545        encoder.dump(self.items)
546
547    def ul4onload(self, decoder):
548        self.items = [tuple(item) for item in decoder.load()]
549
550
551@register("dictcomp")
552class DictComp(AST):
553    """
554    AST node for dictionary comprehension.
555    """
556
557    precedence = 11
558    fields = AST.fields.union({"key", "value", "varname", "container", "condition"})
559
560    def __init__(self, key=None, value=None, varname=None, container=None, condition=None):
561        self.key = key
562        self.value = value
563        self.varname = varname
564        self.container = container
565        self.condition = condition
566
567    def __repr__(self):
568        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} key={0.key!r} value={0.value!r} varname={0.varname!r} container={0.container!r}".format(self)
569        if self.condition is not None:
570            s += " {0.condition!r}".format(self)
571        return s + " at {:#x}>".format(id(self))
572
573    def _repr_pretty_(self, p, cycle):
574        if cycle:
575            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
576        else:
577            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
578                p.breakable()
579                p.text("key=")
580                p.pretty(self.key)
581                p.breakable()
582                p.text("value=")
583                p.pretty(self.value)
584                p.breakable()
585                p.text("varname=")
586                p.pretty(self.varname)
587                p.breakable()
588                p.text("container=")
589                p.pretty(self.container)
590                if self.condition is not None:
591                    p.breakable()
592                    p.text("condition=")
593                    p.pretty(self.condition)
594                p.breakable()
595                p.text("at {:#x}".format(id(self)))
596
597    def iternodes(self):
598        yield self
599        yield from self.key.iternodes()
600        yield from self.value.iternodes()
601        yield from self.container.iternodes()
602        if self.condition is not None:
603            yield from self.condition.iternodes()
604
605    def format(self, indent, keepws):
606        s = "{{{} : {} for {} in {}".format(self.key.format(indent, keepws), self.value.format(indent, keepws), _formatnestednameul4(self.varname), self.container.format(indent, keepws))
607        if self.condition is not None:
608            s += " if {}".format(self.condition.format(indent, keepws))
609        s += "}"
610        return s
611
612    def formatpython(self, indent, keepws):
613        s = "{{{} : {} for {} in {}".format(self.key.formatpython(indent, keepws), self.value.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws))
614        if self.condition is not None:
615            s += " if {}".format(self.condition.formatpython(indent, keepws))
616        s += "}"
617        return s
618
619    def ul4ondump(self, encoder):
620        encoder.dump(self.key)
621        encoder.dump(self.value)
622        encoder.dump(self.varname)
623        encoder.dump(self.container)
624        encoder.dump(self.condition)
625
626    def ul4onload(self, decoder):
627        self.key = decoder.load()
628        self.value = decoder.load()
629        self.varname = decoder.load()
630        self.container = decoder.load()
631        self.condition = decoder.load()
632
633
634@register("genexpr")
635class GenExpr(AST):
636    """
637    AST node for a generator expression.
638    """
639
640    precedence = 11
641    fields = AST.fields.union({"item", "varname", "container", "condition"})
642
643    def __init__(self, item=None, varname=None, container=None, condition=None):
644        self.item = item
645        self.varname = varname
646        self.container = container
647        self.condition = condition
648
649    def __repr__(self):
650        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} item={0.item!r} varname={0.varname!r} container={0.container!r}".format(self)
651        if self.condition is not None:
652            s += " condition={0.condition!r}".format(self)
653        return s + " at {:#x}>".format(id(self))
654
655    def _repr_pretty_(self, p, cycle):
656        if cycle:
657            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
658        else:
659            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
660                p.breakable()
661                p.text("item=")
662                p.pretty(self.item)
663                p.breakable()
664                p.text("varname=")
665                p.pretty(self.varname)
666                p.breakable()
667                p.text("container=")
668                p.pretty(self.container)
669                if self.condition is not None:
670                    p.breakable()
671                    p.text("condition=")
672                    p.pretty(self.condition)
673                p.breakable()
674                p.text("at {:#x}".format(id(self)))
675
676    def iternodes(self):
677        yield self
678        yield from self.item.iternodes()
679        yield from self.container.iternodes()
680        if self.condition is not None:
681            yield from self.condition.iternodes()
682
683    def format(self, indent, keepws):
684        s = "({} for {} in {}".format(self.item.format(indent, keepws), _formatnestednameul4(self.varname), self.container.format(indent, keepws))
685        if self.condition is not None:
686            s += " if {}".format(self.condition.format(indent, keepws))
687        s += ")"
688        return s
689
690    def formatpython(self, indent, keepws):
691        s = "({} for {} in {}".format(self.item.formatpython(indent, keepws), _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws))
692        if self.condition is not None:
693            s += " if {}".format(self.condition.formatpython(indent, keepws))
694        s += ")"
695        return s
696
697    def ul4ondump(self, encoder):
698        encoder.dump(self.item)
699        encoder.dump(self.varname)
700        encoder.dump(self.container)
701        encoder.dump(self.condition)
702
703    def ul4onload(self, decoder):
704        self.item = decoder.load()
705        self.varname = decoder.load()
706        self.container = decoder.load()
707        self.condition = decoder.load()
708
709
710@register("var")
711class Var(AST):
712    """
713    AST nodes for loading a variable.
714    """
715
716    precedence = 11
717    fields = AST.fields.union({"name"})
718
719    def __init__(self, name=None):
720        self.name = name
721
722    def __repr__(self):
723        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.name!r} at {1:#x}>".format(self, id(self))
724
725    def format(self, indent, keepws):
726        return self.name
727
728    def formatpython(self, indent, keepws):
729        return "ul4c._getitem(allvars, {!r})".format(self.name)
730
731    def ul4ondump(self, encoder):
732        encoder.dump(self.name)
733
734    def ul4onload(self, decoder):
735        self.name = decoder.load()
736
737
738class Block(Tag):
739    """
740    Base class for all AST nodes that are blocks.
741
742    A block contains a sequence of tags that are executed sequencially.
743    A block may execute its content zero (e.g. an ``<?if?>`` block) or more times
744    (e.g. a ``<?for?>`` block).
745    """
746
747    fields = Tag.fields.union({"endlocation", "content"})
748
749    def __init__(self, location=None):
750        super().__init__(location)
751        self.endlocation = None
752        self.content = []
753
754    def iternodes(self):
755        yield self
756        for node in self.content:
757            yield from node.iternodes()
758
759    def append(self, item):
760        self.content.append(item)
761
762    def format(self, indent, keepws):
763        v = []
764        v.append("{}{{\n".format(indent*"\t"))
765        indent += 1
766        for node in self.content:
767            v.append(node.format(indent, keepws))
768        indent -= 1
769        v.append("{}}}\n".format(indent*"\t"))
770        return "".join(v)
771
772    def ul4ondump(self, encoder):
773        super().ul4ondump(encoder)
774        encoder.dump(self.endlocation)
775        encoder.dump(self.content)
776
777    def ul4onload(self, decoder):
778        super().ul4onload(decoder)
779        self.endlocation = decoder.load()
780        self.content = decoder.load()
781
782
783@register("ieie")
784class IfElIfElse(Block):
785    """
786    AST node for an conditional block.
787
788    The content of the :class:`IfElIfElse` block is one :class:`If` block
789    followed by zero or more :class:`ElIf` blocks followed by zero or one
790    :class:`Else` block.
791    """
792    def __init__(self, location=None, condition=None):
793        super().__init__(location)
794        if condition is not None:
795            self.newblock(If(location, condition))
796
797    def __repr__(self):
798        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {1} at {2:#x}>".format(self, repr(self.content)[1:-1], id(self))
799
800    def _repr_pretty_(self, p, cycle):
801        if cycle:
802            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
803        else:
804            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
805                for node in self.content:
806                    p.breakable()
807                    p.pretty(node)
808                p.breakable()
809                p.text("at {:#x}".format(id(self)))
810
811    def append(self, item):
812        self.content[-1].append(item)
813
814    def newblock(self, block):
815        if self.content:
816            self.content[-1].endlocation = block.location
817        self.content.append(block)
818
819    def format(self, indent, keepws):
820        v = []
821        for node in self.content:
822            v.append(node.format(indent, keepws))
823        return "".join(v)
824
825    def formatpython(self, indent, keepws):
826        v = []
827        for node in self.content:
828            v.append(node.formatpython(indent, keepws))
829        return "".join(v)
830
831
832@register("if")
833class If(Block):
834    """
835    AST node for an ``<?if?>`` block.
836    """
837
838    fields = Block.fields.union({"condition"})
839
840    def __init__(self, location=None, condition=None):
841        super().__init__(location)
842        self.condition = condition
843
844    def __repr__(self):
845        return "<{0.__class__.__module__}.{0.__class__.__qualname__} condition={0.condition!r} {1} at {2:#x}>".format(self, " ..." if self.content else "", id(self))
846
847    def _repr_pretty_(self, p, cycle):
848        if cycle:
849            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
850        else:
851            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
852                p.breakable()
853                p.text("condition=")
854                p.pretty(self.condition)
855                for node in self.content:
856                    p.breakable()
857                    p.pretty(node)
858                p.breakable()
859                p.text("at {:#x}".format(id(self)))
860
861    def iternodes(self):
862        yield self
863        yield from self.condition.iternodes()
864        for node in self.content:
865            yield from node.iternodes()
866
867    def format(self, indent, keepws):
868        return "{}if {}\n{}".format(indent*"\t", self.condition.format(indent, keepws), super().format(indent, keepws))
869
870    def formatpython(self, indent, keepws):
871        v = ["{i}# <?if?> tag at position {l.starttag}:{l.endtag} ({id})\n".format(i=indent*"\t", id=id(self), l=self.location)]
872        v.append("{}if {}:\n".format(indent*"\t", self.condition.formatpython(indent, keepws)))
873        indent += 1
874        for node in self.content:
875            v.append(node.formatpython(indent, keepws))
876        return "".join(v)
877
878    def ul4ondump(self, encoder):
879        super().ul4ondump(encoder)
880        encoder.dump(self.condition)
881
882    def ul4onload(self, decoder):
883        super().ul4onload(decoder)
884        self.condition = decoder.load()
885
886
887@register("elif")
888class ElIf(Block):
889    """
890    AST node for an ``<?elif?>`` block.
891    """
892
893    fields = Block.fields.union({"condition"})
894
895    def __init__(self, location=None, condition=None):
896        super().__init__(location)
897        self.condition = condition
898
899    def __repr__(self):
900        return "<{0.__class__.__module__}.{0.__class__.__qualname__} condition={0.condition!r} {1} at {2:#x}>".format(self, " ..." if self.content else "", id(self))
901
902    def _repr_pretty_(self, p, cycle):
903        if cycle:
904            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
905        else:
906            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
907                p.breakable()
908                p.text("condition=")
909                p.pretty(self.condition)
910                for node in self.content:
911                    p.breakable()
912                    p.pretty(node)
913                p.breakable()
914                p.text("at {:#x}".format(id(self)))
915
916    def iternodes(self):
917        yield self
918        yield from self.condition.iternodes()
919        for node in self.content:
920            yield from node.iternodes()
921
922    def format(self, indent, keepws):
923        return "{}elif {}\n{}".format(indent*"\t", self.condition.format(indent, keepws), super().format(indent, keepws))
924
925    def formatpython(self, indent, keepws):
926        v = ["{i}# <?elif?> tag at position {l.starttag}:{l.endtag} ({id})\n".format(i=indent*"\t", id=id(self), l=self.location)]
927        v.append("{}elif {}:\n".format(indent*"\t", self.condition.formatpython(indent, keepws)))
928        indent += 1
929        for node in self.content:
930            v.append(node.formatpython(indent, keepws))
931        return "".join(v)
932
933    def ul4ondump(self, encoder):
934        super().ul4ondump(encoder)
935        encoder.dump(self.condition)
936
937    def ul4onload(self, decoder):
938        super().ul4onload(decoder)
939        self.condition = decoder.load()
940
941
942@register("else")
943class Else(Block):
944    """
945    AST node for an ``<?else?>`` block.
946    """
947
948    def __repr__(self):
949        return "<{0.__class__.__module__}.{0.__class__.__qualname__} condition={0.condition!r} at {1:#x}>".format(self, id(self))
950
951    def _repr_pretty_(self, p, cycle):
952        if cycle:
953            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
954        else:
955            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
956                for node in self.content:
957                    p.breakable()
958                    p.pretty(node)
959                p.breakable()
960                p.text("at {:#x}".format(id(self)))
961
962    def format(self, indent, keepws):
963        return "{}else\n{}".format(indent*"\t", super().format(indent, keepws))
964
965    def formatpython(self, indent, keepws):
966        v = ["{i}# <?else?> tag at position {l.starttag}:{l.endtag} ({id})\n".format(i=indent*"\t", id=id(self), l=self.location)]
967        v.append("{}else:\n".format(indent*"\t"))
968        indent += 1
969        for node in self.content:
970            v.append(node.formatpython(indent, keepws))
971        return "".join(v)
972
973
974@register("for")
975class For(Block):
976    """
977    AST node for a ``<?for?>`` loop variable.
978    """
979
980    fields = Block.fields.union({"varname", "container"})
981
982    def __init__(self, location=None, varname=None, container=None):
983        super().__init__(location)
984        self.varname = varname
985        self.container = container
986
987    def __repr__(self):
988        return "<{0.__class__.__module__}.{0.__class__.__qualname__} varname={0.varname!r} container={0.container!r} {1} at {2:#x}>".format(self, " ..." if self.content else "", id(self))
989
990    def _repr_pretty_(self, p, cycle):
991        if cycle:
992            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
993        else:
994            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
995                p.breakable()
996                p.text("varname=")
997                p.pretty(self.varname)
998                p.breakable()
999                p.text("container=")
1000                p.pretty(self.container)
1001                for node in self.content:
1002                    p.breakable()
1003                    p.pretty(node)
1004                p.breakable()
1005                p.text("at {:#x}".format(id(self)))
1006
1007    def iternodes(self):
1008        yield self
1009        yield from self.container.iternodes()
1010        for node in self.content:
1011            yield from node.iternodes()
1012
1013    def ul4ondump(self, encoder):
1014        super().ul4ondump(encoder)
1015        encoder.dump(self.varname)
1016        encoder.dump(self.container)
1017
1018    def ul4onload(self, decoder):
1019        super().ul4onload(decoder)
1020        self.varname = decoder.load()
1021        self.container = decoder.load()
1022
1023    def format(self, indent, keepws):
1024        return "{}for {} in {}\n{}".format(indent*"\t", _formatnestednameul4(self.varname), self.container.format(indent, keepws), super().format(indent, keepws))
1025
1026    def formatpython(self, indent, keepws):
1027        v = ["{i}# <?for?> tag at position {l.starttag}:{l.endtag} ({id})\n".format(i=indent*"\t", id=id(self), l=self.location)]
1028        v.append("{}for {} in {}:\n".format(indent*"\t", _formatnestednamepython(self.varname), self.container.formatpython(indent, keepws)))
1029        indent += 1
1030        if self.content:
1031            for node in self.content:
1032                v.append(node.formatpython(indent, keepws))
1033        else:
1034            # Make sure we have a proper loop body
1035            v.append("{}pass\n".format(indent*"\t"))
1036        return "".join(v)
1037
1038
1039@register("break")
1040class Break(Tag):
1041    """
1042    AST node for a ``<?break?>`` inside a ``<?for?>`` block.
1043    """
1044
1045    def format(self, indent, keepws):
1046        return "{}break\n".format(indent*"\t")
1047
1048    def formatpython(self, indent, keepws):
1049        return "{i}# <?break?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}break\n".format(i=indent*"\t", id=id(self), l=self.location)
1050
1051
1052@register("continue")
1053class Continue(Tag):
1054    """
1055    AST node for a ``<?continue?>`` inside a ``<?for?>`` block.
1056    """
1057
1058    def format(self, indent, keepws):
1059        return "{}continue\n".format(indent*"\t")
1060
1061    def formatpython(self, indent, keepws):
1062        return "{i}# <?continue?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}continue\n".format(i=indent*"\t", id=id(self), l=self.location)
1063
1064
1065@register("getattr")
1066class GetAttr(AST):
1067    """
1068    AST node for getting an attribute from an object.
1069
1070    The object is loaded from the AST node :var:`obj` and the attribute name
1071    is stored in the string :var:`attrname`.
1072    """
1073    precedence = 9
1074    associative = False
1075    fields = AST.fields.union({"obj", "attrname"})
1076
1077    def __init__(self, obj=None, attrname=None):
1078        self.obj = obj
1079        self.attrname = attrname
1080
1081    def __repr__(self):
1082        return "<{0.__class__.__module__}.{0.__class__.__qualname__} obj={0.obj!r}, attrname={0.attrname!r} at {1:#x}>".format(self, id(self))
1083
1084    def _repr_pretty_(self, p, cycle):
1085        if cycle:
1086            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1087        else:
1088            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1089                p.breakable()
1090                p.text("obj=")
1091                p.pretty(self.obj)
1092                p.breakable()
1093                p.text("attrname=")
1094                p.pretty(self.attrname)
1095                p.breakable()
1096                p.text("at {:#x}".format(id(self)))
1097
1098    def iternodes(self):
1099        yield self
1100        yield from self.obj.iternodes()
1101
1102    def format(self, indent, keepws):
1103        return "{}.{}".format(self._formatop(self.obj), self.attrname)
1104
1105    def formatpython(self, indent, keepws):
1106        return "ul4c._getitem({}, {!r})".format(self.obj.formatpython(indent, keepws), self.attrname)
1107
1108    def ul4ondump(self, encoder):
1109        encoder.dump(self.obj)
1110        encoder.dump(self.attrname)
1111
1112    def ul4onload(self, decoder):
1113        self.obj = decoder.load()
1114        self.attrname = decoder.load()
1115
1116
1117@register("getslice")
1118class GetSlice(AST):
1119    """
1120    AST node for getting a slice from a list or string object.
1121
1122    The object is loaded from the AST node :var:`obj` and the start and stop
1123    indices from the AST node :var:`index1` and :var:`index2`. :var:`index1`
1124    and :var:`index2` may also be :const:`None` (for missing slice indices,
1125    which default to the 0 for the start index and the length of the sequence
1126    for the end index).
1127    """
1128
1129    precedence = 8
1130    associative = False
1131    fields = AST.fields.union({"obj", "index1", "index2"})
1132
1133    def __init__(self, obj=None, index1=None, index2=None):
1134        self.obj = obj
1135        self.index1 = index1
1136        self.index2 = index2
1137
1138    def iternodes(self):
1139        yield self
1140        yield from self.obj.iternodes()
1141        if self.index1 is not None:
1142            yield from self.index1.iternodes()
1143        if self.index2 is not None:
1144            yield from self.index2.iternodes()
1145
1146    def __repr__(self):
1147        return "<{0.__class__.__module__}.{0.__class__.__qualname__} obj={0.obj!r} index1={0.index1!r} index2={0.index2!r} at {1:#x}>".format(self, id(self))
1148
1149    def _repr_pretty_(self, p, cycle):
1150        if cycle:
1151            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1152        else:
1153            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1154                p.breakable()
1155                p.text("obj=")
1156                p.pretty(self.obj)
1157                p.breakable()
1158                p.text("index1=")
1159                p.pretty(self.index1)
1160                p.breakable()
1161                p.text("index2=")
1162                p.pretty(self.index2)
1163                p.breakable()
1164                p.text("at {:#x}".format(id(self)))
1165
1166    @classmethod
1167    def make(cls, obj, index1, index2):
1168        # We don't have to check for undefined results here, because this can't happen with slices
1169        if isinstance(obj, Const):
1170            if index1 is None:
1171                if index2 is None:
1172                    return Const(obj[:])
1173                elif isinstance(index2, Const):
1174                    return Const(obj[:index2.value])
1175            elif isinstance(index1, Const):
1176                if index2 is None:
1177                    return Const(obj[index1.value:])
1178                elif isinstance(index2, Const):
1179                    return Const(obj[index1.value:index2.value])
1180        return cls(obj, index1, index2)
1181
1182    def format(self, indent, keepws):
1183        return "{}[{}:{}]".format(self._formatop(self.obj), self.index1.format(indent, keepws) if self.index1 is not None else "", self.index2.format(indent, keepws) if self.index2 is not None else "")
1184
1185    def formatpython(self, indent, keepws):
1186        return "({})[{}:{}]".format(self.obj.formatpython(indent, keepws), self.index1.formatpython(indent, keepws) if self.index1 is not None else "", self.index2.formatpython(indent, keepws) if self.index2 is not None else "")
1187
1188    def ul4ondump(self, encoder):
1189        encoder.dump(self.obj)
1190        encoder.dump(self.index1)
1191        encoder.dump(self.index2)
1192
1193    def ul4onload(self, decoder):
1194        self.obj = decoder.load()
1195        self.index1 = decoder.load()
1196        self.index2 = decoder.load()
1197
1198
1199class Unary(AST):
1200    """
1201    Base class for all AST nodes implementing unary operators.
1202    """
1203
1204    fields = AST.fields.union({"obj"})
1205
1206    def __init__(self, obj=None):
1207        self.obj = obj
1208
1209    def iternodes(self):
1210        yield self
1211        yield from self.obj.iternodes()
1212
1213    def __repr__(self):
1214        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.obj!r} at {1:#x}>".format(self, id(self))
1215
1216    def _repr_pretty_(self, p, cycle):
1217        if cycle:
1218            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1219        else:
1220            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1221                p.breakable()
1222                p.pretty(self.obj)
1223                p.breakable()
1224                p.text("at {:#x}".format(id(self)))
1225
1226    def ul4ondump(self, encoder):
1227        encoder.dump(self.obj)
1228
1229    def ul4onload(self, decoder):
1230        self.obj = decoder.load()
1231
1232    @classmethod
1233    def make(cls, obj):
1234        if isinstance(obj, Const):
1235            result = cls.evaluate(obj.value)
1236            if not isinstance(result, Undefined):
1237                return Const(result)
1238        return cls(obj)
1239
1240
1241@register("not")
1242class Not(Unary):
1243    """
1244    AST node for the unary ``not`` operator.
1245    """
1246
1247    precedence = 2
1248
1249    @classmethod
1250    def evaluate(cls, obj):
1251        return not obj
1252
1253    def format(self, indent, keepws):
1254        return "not {}".format(self._formatop(self.obj))
1255
1256    def formatpython(self, indent, keepws):
1257        return "not ({})".format(self.obj.formatpython(indent, keepws))
1258
1259
1260@register("neg")
1261class Neg(Unary):
1262    """
1263    AST node for the unary negation (i.e. "-") operator.
1264    """
1265
1266    precedence = 7
1267
1268    @classmethod
1269    def evaluate(cls, obj):
1270        return -obj
1271
1272    def format(self, indent, keepws):
1273        return "-{}".format(self._formatop(self.obj))
1274
1275    def formatpython(self, indent, keepws):
1276        return "-({})".format(self.obj.formatpython(indent, keepws))
1277
1278
1279class UnaryTag(Tag):
1280    fields = Tag.fields.union({"obj"})
1281
1282    def __init__(self, location=None, obj=None):
1283        super().__init__(location)
1284        self.obj = obj
1285
1286    def __repr__(self):
1287        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.obj!r} at {1:#x}>".format(self, id(self))
1288
1289    def _repr_pretty_(self, p, cycle):
1290        if cycle:
1291            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1292        else:
1293            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1294                p.breakable()
1295                p.pretty(self.obj)
1296                p.breakable()
1297                p.text("at {:#x}".format(id(self)))
1298
1299    def iternodes(self):
1300        yield self
1301        yield from self.obj.iternodes()
1302
1303    def ul4ondump(self, encoder):
1304        super().ul4ondump(encoder)
1305        encoder.dump(self.obj)
1306
1307    def ul4onload(self, decoder):
1308        super().ul4onload(decoder)
1309        self.obj = decoder.load()
1310
1311
1312@register("return")
1313class Return(UnaryTag):
1314    """
1315    AST node for a ``<?return?>`` tag.
1316    """
1317
1318    def format(self, indent, keepws):
1319        return "{}return {}\n".format(indent*"\t", self.obj.format(indent, keepws))
1320
1321    def formatpython(self, indent, keepws):
1322        return "{i}# <?return?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}return {o}\n".format(i=indent*"\t", id=id(self), o=self.obj.formatpython(indent, keepws), l=self.location)
1323
1324
1325@register("print")
1326class Print(UnaryTag):
1327    """
1328    AST node for a ``<?print?>`` tag.
1329    """
1330
1331    def format(self, indent, keepws):
1332        return "{}print {}\n".format(indent*"\t", self.obj.format(indent, keepws))
1333
1334    def formatpython(self, indent, keepws):
1335        return "{i}# <?print?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}yield ul4c._str({o})\n".format(i=indent*"\t", id=id(self), o=self.obj.formatpython(indent, keepws), l=self.location)
1336
1337
1338@register("printx")
1339class PrintX(UnaryTag):
1340    """
1341    AST node for a ``<?printx?>`` tag.
1342    """
1343
1344    def format(self, indent, keepws):
1345        return "{}printx {}\n".format(indent*"\t", self.obj.format(indent, keepws))
1346
1347    def formatpython(self, indent, keepws):
1348        return "{i}# <?printx?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}yield ul4c._xmlescape({o})\n".format(i=indent*"\t", id=id(self), o=self.obj.formatpython(indent, keepws), l=self.location)
1349
1350
1351class Binary(AST):
1352    """
1353    Base class for all AST nodes implementing binary operators.
1354    """
1355
1356    fields = AST.fields.union({"obj1", "obj2"})
1357
1358    def __init__(self, obj1=None, obj2=None):
1359        self.obj1 = obj1
1360        self.obj2 = obj2
1361
1362    def iternodes(self):
1363        yield self
1364        yield from self.obj1.iternodes()
1365        yield from self.obj2.iternodes()
1366
1367    def __repr__(self):
1368        return "<{0.__class__.__module__}.{0.__class__.__qualname__} {0.obj1!r} {0.obj2!r} at {1:#x}>".format(self, id(self))
1369
1370    def _repr_pretty_(self, p, cycle):
1371        if cycle:
1372            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1373        else:
1374            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1375                p.breakable()
1376                p.pretty(self.obj1)
1377                p.breakable()
1378                p.pretty(self.obj2)
1379                p.breakable()
1380                p.text("at {:#x}".format(id(self)))
1381
1382    def ul4ondump(self, encoder):
1383        encoder.dump(self.obj1)
1384        encoder.dump(self.obj2)
1385
1386    def ul4onload(self, decoder):
1387        self.obj1 = decoder.load()
1388        self.obj2 = decoder.load()
1389
1390    @classmethod
1391    def make(cls, obj1, obj2):
1392        if isinstance(obj1, Const) and isinstance(obj2, Const):
1393            result = cls.evaluate(obj1.value, obj2.value)
1394            if not isinstance(result, Undefined):
1395                return Const(result)
1396        return cls(obj1, obj2)
1397
1398
1399@register("getitem")
1400class GetItem(Binary):
1401    """
1402    AST node for subscripting operator.
1403
1404    The object (which must be a list, string or dict) is loaded from the AST
1405    node :var:`obj1` and the index/key is loaded from the AST node :var:`obj2`.
1406    """
1407
1408    precedence = 9
1409    associative = False
1410
1411    @classmethod
1412    def evaluate(cls, obj1, obj2):
1413        return obj1[obj2]
1414
1415    def format(self, indent, keepws):
1416        return "{}[{}]".format(self._formatop(self.obj1), self.obj2.format(indent, keepws))
1417
1418    def formatpython(self, indent, keepws):
1419        return "ul4c._getitem({}, {})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1420
1421
1422@register("eq")
1423class EQ(Binary):
1424    """
1425    AST node for the binary ``==`` comparison operator.
1426    """
1427
1428    precedence = 4
1429    associative = False
1430
1431    @classmethod
1432    def evaluate(cls, obj1, obj2):
1433        return obj1 == obj2
1434
1435    def format(self, indent, keepws):
1436        return "{} == {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1437
1438    def formatpython(self, indent, keepws):
1439        return "({}) == ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1440
1441
1442@register("ne")
1443class NE(Binary):
1444    """
1445    AST node for the binary ``!=`` comparison operator.
1446    """
1447
1448    precedence = 4
1449    associative = False
1450
1451    @classmethod
1452    def evaluate(cls, obj1, obj2):
1453        return obj1 != obj2
1454
1455    def format(self, indent, keepws):
1456        return "{} != {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1457
1458    def formatpython(self, indent, keepws):
1459        return "({}) != ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1460
1461
1462@register("lt")
1463class LT(Binary):
1464    """
1465    AST node for the binary ``<`` comparison operator.
1466    """
1467
1468    precedence = 4
1469    associative = False
1470
1471    @classmethod
1472    def evaluate(cls, obj1, obj2):
1473        return obj1 < obj2
1474
1475    def format(self, indent, keepws):
1476        return "{} < {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1477
1478    def formatpython(self, indent, keepws):
1479        return "({}) < ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1480
1481
1482@register("le")
1483class LE(Binary):
1484    """
1485    AST node for the binary ``<=`` comparison operator.
1486    """
1487
1488    precedence = 4
1489    associative = False
1490
1491    @classmethod
1492    def evaluate(cls, obj1, obj2):
1493        return obj1 <= obj2
1494
1495    def format(self, indent, keepws):
1496        return "{} <= {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1497
1498    def formatpython(self, indent, keepws):
1499        return "({}) <= ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1500
1501
1502@register("gt")
1503class GT(Binary):
1504    """
1505    AST node for the binary ``>`` comparison operator.
1506    """
1507
1508    precedence = 4
1509    associative = False
1510
1511    @classmethod
1512    def evaluate(cls, obj1, obj2):
1513        return obj1 > obj2
1514
1515    def format(self, indent, keepws):
1516        return "{} > {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1517
1518    def formatpython(self, indent, keepws):
1519        return "({}) > ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1520
1521
1522@register("ge")
1523class GE(Binary):
1524    """
1525    AST node for the binary ``>=`` comparison operator.
1526    """
1527
1528    precedence = 4
1529    associative = False
1530
1531    @classmethod
1532    def evaluate(cls, obj1, obj2):
1533        return obj1 >= obj2
1534
1535    def format(self, indent, keepws):
1536        return "{} >= {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1537
1538    def formatpython(self, indent, keepws):
1539        return "({}) >= ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1540
1541
1542@register("contains")
1543class Contains(Binary):
1544    """
1545    AST node for the binary containment testing operator.
1546
1547    The item/key object is loaded from the AST node :var:`obj1` and the container
1548    object (which must be a list, string or dict) is loaded from the AST node
1549    :var:`obj2`.
1550    """
1551
1552    precedence = 3
1553    associative = False
1554
1555    @classmethod
1556    def evaluate(cls, obj1, obj2):
1557        return obj1 in obj2
1558
1559    def format(self, indent, keepws):
1560        return "{} in {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1561
1562    def formatpython(self, indent, keepws):
1563        return "({}) in ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1564
1565
1566@register("notcontains")
1567class NotContains(Binary):
1568    """
1569    AST node for the inverted containment testing operator.
1570
1571    The item/key object is loaded from the AST node :var:`obj1` and the container
1572    object (which must be a list, string or dict) is loaded from the AST node
1573    :var:`obj2`.
1574    """
1575
1576    precedence = 3
1577    associative = False
1578
1579    @classmethod
1580    def evaluate(cls, obj1, obj2):
1581        return obj1 not in obj2
1582
1583    def format(self, indent, keepws):
1584        return "{} not in {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1585
1586    def formatpython(self, indent, keepws):
1587        return "({}) not in ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1588
1589
1590@register("add")
1591class Add(Binary):
1592    """
1593    AST node for the binary addition operator.
1594    """
1595
1596    precedence = 5
1597
1598    @classmethod
1599    def evaluate(cls, obj1, obj2):
1600        return obj1 + obj2
1601
1602    def format(self, indent, keepws):
1603        return "{}+{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1604
1605    def formatpython(self, indent, keepws):
1606        return "({}) + ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1607
1608
1609@register("sub")
1610class Sub(Binary):
1611    """
1612    AST node for the binary substraction operator.
1613    """
1614
1615    precedence = 5
1616    associative = False
1617
1618    @classmethod
1619    def evaluate(cls, obj1, obj2):
1620        return obj1 - obj2
1621
1622    def format(self, indent, keepws):
1623        return "{}-{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1624
1625    def formatpython(self, indent, keepws):
1626        return "({}) - ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1627
1628
1629@register("mul")
1630class Mul(Binary):
1631    """
1632    AST node for the binary multiplication operator.
1633    """
1634
1635    precedence = 6
1636
1637    @classmethod
1638    def evaluate(cls, obj1, obj2):
1639        return obj1 * obj2
1640
1641    def format(self, indent, keepws):
1642        return "{}*{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1643
1644    def formatpython(self, indent, keepws):
1645        return "({}) * ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1646
1647
1648@register("floordiv")
1649class FloorDiv(Binary):
1650    """
1651    AST node for the binary truncating division operator.
1652    """
1653
1654    precedence = 6
1655    associative = False
1656
1657    @classmethod
1658    def evaluate(cls, obj1, obj2):
1659        return obj1 // obj2
1660
1661    def format(self, indent, keepws):
1662        return "{}//{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1663
1664    def formatpython(self, indent, keepws):
1665        return "({}) // ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1666
1667
1668@register("truediv")
1669class TrueDiv(Binary):
1670    """
1671    AST node for the binary true division operator.
1672    """
1673
1674    precedence = 6
1675    associative = False
1676
1677    @classmethod
1678    def evaluate(cls, obj1, obj2):
1679        return obj1 / obj2
1680
1681    def format(self, indent, keepws):
1682        return "{}/{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1683
1684    def formatpython(self, indent, keepws):
1685        return "({}) / ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1686
1687
1688@register("and")
1689class And(Binary):
1690    """
1691    AST node for the binary ``and`` operator.
1692    """
1693
1694    precedence = 1
1695
1696    @classmethod
1697    def evaluate(cls, obj1, obj2):
1698        return obj1 and obj2
1699
1700    def format(self, indent, keepws):
1701        return "{} and {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1702
1703    def formatpython(self, indent, keepws):
1704        return "({}) and ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1705
1706
1707@register("or")
1708class Or(Binary):
1709    """
1710    AST node for the binary ``or`` operator.
1711    """
1712
1713    precedence = 0
1714
1715    @classmethod
1716    def evaluate(cls, obj1, obj2):
1717        return obj1 or obj2
1718
1719    def format(self, indent, keepws):
1720        return "{} or {}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1721
1722    def formatpython(self, indent, keepws):
1723        return "({}) or ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1724
1725
1726@register("mod")
1727class Mod(Binary):
1728    """
1729    AST node for the binary modulo operator.
1730    """
1731
1732    precedence = 6
1733    associative = False
1734
1735    @classmethod
1736    def evaluate(cls, obj1, obj2):
1737        return obj1 % obj2
1738
1739    def format(self, indent, keepws):
1740        return "{}%{}".format(self._formatop(self.obj1), self._formatop(self.obj2))
1741
1742    def formatpython(self, indent, keepws):
1743        return "({}) % ({})".format(self.obj1.formatpython(indent, keepws), self.obj2.formatpython(indent, keepws))
1744
1745
1746class ChangeVar(Tag):
1747    """
1748    Baseclass for all AST nodes that store or modify a variable.
1749
1750    The variable name is stored in the string :var:`varname` and the value that
1751    will be stored or be used to modify the stored value is loaded from the
1752    AST node :var:`value`.
1753    """
1754
1755    fields = Tag.fields.union({"varname", "value"})
1756
1757    def __init__(self, location=None, varname=None, value=None):
1758        super().__init__(location)
1759        self.varname = varname
1760        self.value = value
1761
1762    def __repr__(self):
1763        return "<{0.__class__.__module__}.{0.__class__.__qualname__} varname={0.varname!r} value={0.value!r} at {1:#x}>".format(self, id(self))
1764
1765    def _repr_pretty_(self, p, cycle):
1766        if cycle:
1767            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1768        else:
1769            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1770                p.breakable()
1771                p.text("varname=")
1772                p.pretty(self.obj1)
1773                p.text("value=")
1774                p.breakable()
1775                p.pretty(self.obj2)
1776                p.breakable()
1777                p.text("at {:#x}".format(id(self)))
1778
1779    def iternodes(self):
1780        yield self
1781        yield from self.value.iternodes()
1782
1783    def ul4ondump(self, encoder):
1784        super().ul4ondump(encoder)
1785        encoder.dump(self.varname)
1786        encoder.dump(self.value)
1787
1788    def ul4onload(self, decoder):
1789        super().ul4onload(decoder)
1790        self.varname = decoder.load()
1791        self.value = decoder.load()
1792
1793
1794@register("storevar")
1795class StoreVar(ChangeVar):
1796    """
1797    AST node that stores a value into a variable.
1798    """
1799
1800    def format(self, indent, keepws):
1801        return "{}{} = {}\n".format(indent*"\t", _formatnestednameul4(self.varname), self.value.format(indent, keepws))
1802
1803    def formatpython(self, indent, keepws):
1804        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}{n} = {v}\n".format(i=indent*"\t", id=id(self), n=_formatnestednamepython(self.varname), v=self.value.formatpython(indent, keepws), l=self.location)
1805
1806
1807@register("addvar")
1808class AddVar(ChangeVar):
1809    """
1810    AST node that adds a value to a variable (i.e. the ``+=`` operator).
1811    """
1812
1813    def format(self, indent, keepws):
1814        return "{}{} += {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1815
1816    def formatpython(self, indent, keepws):
1817        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] += {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1818
1819
1820@register("subvar")
1821class SubVar(ChangeVar):
1822    """
1823    AST node that substracts a value from a variable (i.e. the ``-=`` operator).
1824    """
1825
1826    def format(self, indent, keepws):
1827        return "{}{} -= {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1828
1829    def formatpython(self, indent, keepws):
1830        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] -= {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1831
1832
1833@register("mulvar")
1834class MulVar(ChangeVar):
1835    """
1836    AST node that multiplies a variable by a value (i.e. the ``*=`` operator).
1837    """
1838
1839    def format(self, indent, keepws):
1840        return "{}{} *= {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1841
1842    def formatpython(self, indent, keepws):
1843        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] *= {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1844
1845
1846@register("floordivvar")
1847class FloorDivVar(ChangeVar):
1848    """
1849    AST node that divides a variable by a value (truncating to an integer value;
1850    i.e. the ``//=`` operator).
1851    """
1852
1853    def format(self, indent, keepws):
1854        return "{}{} //= {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1855
1856    def formatpython(self, indent, keepws):
1857        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] //= {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1858
1859
1860@register("truedivvar")
1861class TrueDivVar(ChangeVar):
1862    """
1863    AST node that divides a variable by a value (i.e. the ``/=`` operator).
1864    """
1865
1866    def format(self, indent, keepws):
1867        return "{}{} /= {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1868
1869    def formatpython(self, indent, keepws):
1870        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] /= {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1871
1872
1873@register("modvar")
1874class ModVar(ChangeVar):
1875    """
1876    AST node for the ``%=`` operator.
1877    """
1878
1879    def format(self, indent, keepws):
1880        return "{}{} %= {}\n".format(indent*"\t", self.varname, self.value.format(indent, keepws))
1881
1882    def formatpython(self, indent, keepws):
1883        return "{i}# <?code?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] %= {v}\n".format(i=indent*"\t", id=id(self), n=self.varname, v=self.value.formatpython(indent, keepws), l=self.location)
1884
1885
1886@register("callfunc")
1887class CallFunc(AST):
1888    """
1889    AST node for calling an function.
1890
1891    The object to be called is stored in the attribute :var:`obj`. The list of
1892    positional arguments is loaded from the list of AST nodes :var:`args`.
1893    Keyword arguments are in :var:`kwargs`. `var`:remargs` is the AST node
1894    for the ``*`` argument (and may by ``None`` if there is no ``*`` argument).
1895    `var`:remkwargs` is the AST node for the ``**`` argument (and may by ``None``
1896    if there is no ``**`` argument)
1897    """
1898
1899    precedence = 10
1900    associative = False
1901    fields = AST.fields.union({"obj", "args", "kwargs", "remargs", "remkwargs"})
1902
1903    def __init__(self, obj=None):
1904        self.obj = obj
1905        self.args = []
1906        self.kwargs = []
1907        self.remargs = None
1908        self.remkwargs = None
1909
1910    def __repr__(self):
1911        return "<{0.__class__.__module__}.{0.__class__.__qualname__} obj={0.obj!r}{1}{2}{3}{4} at {5:#x}>".format(
1912            self,
1913            "".join(" {!r}".format(arg) for arg in self.args),
1914            "".join(" {}={!r}".format(argname, argvalue) for (argname, argvalue) in self.kwargs),
1915            " *{!r}".format(self.remargs) if self.remargs is not None else "",
1916            " **{!r}".format(self.remkwargs) if self.remargs is not None else "",
1917            id(self))
1918
1919    def _repr_pretty_(self, p, cycle):
1920        if cycle:
1921            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
1922        else:
1923            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
1924                p.breakable()
1925                p.text("obj=")
1926                p.pretty(self.obj)
1927                for arg in self.args:
1928                    p.breakable()
1929                    p.pretty(arg)
1930                for (argname, arg) in self.kwargs:
1931                    p.breakable()
1932                    p.text("{}=".format(argname))
1933                    p.pretty(arg)
1934                if self.remargs is not None:
1935                    p.breakable()
1936                    p.text("*")
1937                    p.pretty(self.remargs)
1938                if self.remkwargs is not None:
1939                    p.breakable()
1940                    p.text("**")
1941                    p.pretty(self.remkwargs)
1942                p.breakable()
1943                p.text("at {:#x}".format(id(self)))
1944
1945    def iternodes(self):
1946        yield self
1947        yield from self.obj.iternodes()
1948        for arg in self.args:
1949            yield from arg.iternodes()
1950        for (argname, arg) in self.kwargs:
1951            yield from arg.iternodes()
1952        if self.remargs is not None:
1953            yield from self.remargs.iternodes()
1954        if self.remkwargs is not None:
1955            yield from self.remkwargs.iternodes()
1956
1957    def format(self, indent, keepws):
1958        args = []
1959        for arg in self.args:
1960            s = arg.format(indent, keepws)
1961            if isinstance(arg, GenExpr):
1962                s = s[1:-1]
1963            args.append(s)
1964        for (argname, argvalue) in self.kwargs:
1965            s = argvalue.format(indent, keepws)
1966            if isinstance(argvalue, GenExpr):
1967                s = s[1:-1]
1968            args.append("{}={}".format(argname, s))
1969        if self.remargs is not None:
1970            args.append("*{}".format(self.remargs.format(indent, keepws)))
1971        if self.remkwargs is not None:
1972            args.append("**{}".format(self.remkwargs.format(indent, keepws)))
1973        return "{}({})".format(self.obj.format(indent, keepws), ", ".join(args))
1974
1975    def formatpython(self, indent, keepws):
1976        args = []
1977        for arg in self.args:
1978            args.append(arg.formatpython(indent, keepws))
1979        for (argname, argvalue) in self.kwargs:
1980            args.append("{}={}".format(argname, argvalue.formatpython(indent, keepws)))
1981        if self.remargs is not None:
1982            args.append("*{}".format(self.remargs.formatpython(indent, keepws)))
1983        if self.remkwargs is not None:
1984            args.append("**{}".format(self.remkwargs.formatpython(indent, keepws)))
1985        return "({}({}))".format(self.obj.formatpython(indent, keepws), ", ".join(args))
1986
1987    def ul4ondump(self, encoder):
1988        encoder.dump(self.obj)
1989        encoder.dump(self.args)
1990        encoder.dump(self.kwargs)
1991        encoder.dump(self.remargs)
1992        encoder.dump(self.remkwargs)
1993
1994    def ul4onload(self, decoder):
1995        self.obj = decoder.load()
1996        self.args = decoder.load()
1997        self.kwargs = [tuple(arg) for arg in decoder.load()]
1998        self.remargs = decoder.load()
1999        self.remkwargs = decoder.load()
2000
2001
2002@register("callmeth")
2003class CallMeth(AST):
2004    """
2005    AST node for calling a method.
2006
2007    The method name is stored in the string :var:`methname`. The object for which
2008    the method will be called is loaded from the AST node :var:`obj` and the list
2009    of arguments is loaded from the list of AST nodes :var:`args`. Keyword
2010    arguments are in :var:`kwargs`. `var`:remargs` is the AST node for the ``*``
2011    argument (and may by ``None`` if there is no ``*`` argument).
2012    `var`:remkwargs` is the AST node for the ``**`` argument (and may by ``None``
2013    if there is no ``**`` argument)
2014    """
2015
2016    precedence = 9
2017    associative = False
2018    fields = AST.fields.union({"obj", "methname", "args", "kwargs", "remargs", "remkwargs"})
2019
2020    def __init__(self, obj=None, methname=None):
2021        self.obj = obj
2022        self.methname = methname
2023        self.args = []
2024        self.kwargs = []
2025        self.remargs = None
2026        self.remkwargs = None
2027
2028    def __repr__(self):
2029        return "<{0.__class__.__module__}.{0.__class__.__qualname__} methname={0.methname!r} obj={0.obj!r}{1}{2}{3}{4} at {5:#x}>".format(
2030            self,
2031            "".join(" {!r}".format(arg) for arg in self.args),
2032            "".join(" {}={!r}".format(argname, argvalue) for (argname, argvalue) in self.kwargs),
2033            " *{!r}".format(self.remargs) if self.remargs is not None else "",
2034            " **{!r}".format(self.remkwargs) if self.remargs is not None else "",
2035            id(self))
2036
2037    def _repr_pretty_(self, p, cycle):
2038        if cycle:
2039            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
2040        else:
2041            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
2042                p.breakable()
2043                p.text("methname=")
2044                p.pretty(self.methname)
2045                p.breakable()
2046                p.text("obj=")
2047                p.pretty(self.obj)
2048                for arg in self.args:
2049                    p.breakable()
2050                    p.pretty(arg)
2051                for (argname, arg) in self.kwargs:
2052                    p.breakable()
2053                    p.text("{}=".format(argname))
2054                    p.pretty(arg)
2055                if self.remargs is not None:
2056                    p.breakable()
2057                    p.text("*")
2058                    p.pretty(self.remargs)
2059                if self.remkwargs is not None:
2060                    p.breakable()
2061                    p.text("**")
2062                    p.pretty(self.remkwargs)
2063                p.breakable()
2064                p.text("at {:#x}".format(id(self)))
2065
2066    def iternodes(self):
2067        yield self
2068        for arg in self.args:
2069            yield from arg.iternodes()
2070        for (argname, arg) in self.kwargs:
2071            yield from arg.iternodes()
2072        if self.remargs is not None:
2073            yield from self.remargs.iternodes()
2074        if self.remkwargs is not None:
2075            yield from self.remkwargs.iternodes()
2076
2077    def format(self, indent, keepws):
2078        args = []
2079        if len(self.args) == 1 and isinstance(self.args[0], GenExpr) and not self.kwargs and self.remargs is None and self.remkwargs is None:
2080            args.append(self.args[0].format(indent, keepws)[1:-1])
2081        else:
2082            for arg in self.args:
2083                args.append(arg.format(indent, keepws))
2084            for (argname, argvalue) in self.kwargs:
2085                args.append("{}={}".format(argname, argvalue.format(indent, keepws)))
2086            if self.remargs is not None:
2087                args.append("*{}".format(self.remargs.format(indent, keepws)))
2088            if self.remkwargs is not None:
2089                args.append("**{}".format(self.remkwargs.format(indent, keepws)))
2090        return "{}.{}({})".format(self._formatop(self.obj), self.methname, ", ".join(args))
2091
2092    def formatpython(self, indent, keepws):
2093        args = []
2094        for arg in self.args:
2095            args.append(arg.formatpython(indent, keepws))
2096        for (argname, argvalue) in self.kwargs:
2097            args.append("{}={}".format(argname, argvalue.formatpython(indent, keepws)))
2098        if self.remargs is not None:
2099            args.append("*{}".format(self.remargs.formatpython(indent, keepws)))
2100        if self.remkwargs is not None:
2101            args.append("**{}".format(self.remkwargs.formatpython(indent, keepws)))
2102        return "self.methods[{!r}]({}, {})".format(self.methname, self.obj.formatpython(indent, keepws), ", ".join(args))
2103
2104    def ul4ondump(self, encoder):
2105        encoder.dump(self.methname)
2106        encoder.dump(self.obj)
2107        encoder.dump(self.args)
2108        encoder.dump(self.kwargs)
2109        encoder.dump(self.remargs)
2110        encoder.dump(self.remkwargs)
2111
2112    def ul4onload(self, decoder):
2113        self.methname = decoder.load()
2114        self.obj = decoder.load()
2115        self.args = decoder.load()
2116        self.kwargs = [tuple(arg) for arg in decoder.load()]
2117        self.remargs = decoder.load()
2118        self.remkwargs = decoder.load()
2119
2120
2121@register("render")
2122class Render(UnaryTag):
2123    """
2124    AST node for the ``<?render?>`` tag.
2125    """
2126
2127    def format(self, indent, keepws):
2128        return "{}render {}\n".format(indent*"\t", self.obj.format(indent, keepws))
2129
2130    def formatpython(self, indent, keepws):
2131        if isinstance(self.obj, CallMeth) and self.obj.methname == "render":
2132            code = "yield from {}".format(self.obj.formatpython(indent, keepws))
2133        else:
2134            code = "yield ul4c._str(vars, {})".format(self.obj.formatpython(indent, keepws))
2135        return "{i}# <?render?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}{c}\n".format(i=indent*"\t", id=id(self), c=code, l=self.location)
2136
2137
2138class Code(Block):
2139    """
2140    Base class of :class:`Template` and :class:`Function` that contains the
2141    functionality common to both classes.
2142    """
2143    fields = Block.fields.union({"source", "name", "keepws", "startdelim", "enddelim", "endlocation"})
2144
2145    version = "24"
2146
2147    functions = {}
2148    methods = {}
2149
2150    def __init__(self, source=None, name=None, keepws=True, startdelim="<?", enddelim="?>"):
2151        """
2152        Create a :class:`Code` object. If :var:`source` is ``None``, the
2153        :class:`Code` remains uninitialized, otherwise :var:`source` will be
2154        compiled (using :var:`startdelim` and :var:`enddelim` as the tag
2155        delimiters). :var:`name` is the name of the template/function. It will be
2156        used in exception messages and should be a valid Python identifier. If
2157        :var:`keepws` is false linefeeds and indentation will be ignored in the
2158        literal text in templates (i.e. the text between the tags). However
2159        trailing whitespace at the end of the line will be honored regardless of
2160        the value of :var:`keepws`. Literal text (and ``<?render?>`` tags) wil
2161        always be ignored inside functions.
2162        """
2163        # ``location``/``endlocation`` will remain ``None`` for a top level template/function
2164        # For a subtemplate/subfunction ``location`` will be set to the location of the ``<?template?>``/``<?function?>`` tag
2165        # in :meth:`_compile` and ``endlocation`` will be the location of the ``<?end template?>``/``<?end function?>`` tag
2166        super().__init__(None)
2167        self._keepws = keepws
2168        self.startdelim = startdelim
2169        self.enddelim = enddelim
2170        self.name = name
2171        self.source = None
2172
2173        # The following attributes (``_astsbyid``, ``_pythonsource`` and ``_pythonfunction``)
2174        # are used for converting the AST back to executable Python code
2175        # They will be initialized when required
2176
2177        # ``_astsbyid`` maps the id of the AST node to the ast node itself
2178        # It is used in :meth:`Template.format` and :meth:`Function.format`
2179        # (to give the generated Python source code access to the subtemplate/subfunction)
2180        # and for proper exception chaining (when an exception occurs, comments in the
2181        # generated source code allow finding the offending AST node)
2182        self._astsbyid = {}
2183        # Python source code generated for the template/function
2184        self._pythonsource = None
2185        # A compiled Python function implementing the template/function logic
2186        self._pythonfunction = None
2187
2188        # If we have source code compile it
2189        if source is not None:
2190            self._compile(source, name, startdelim, enddelim)
2191
2192    def __repr__(self):
2193        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} name={0.name!r} keepws={0.keepws!r}".format(self)
2194        if self.startdelim != "<?":
2195            s += " startdelim={0.startdelim!r}".format(self)
2196        if self.enddelim != "?>":
2197            s += " enddelim={0.enddelim!r}".format(self)
2198        if self.content:
2199            s + " ..."
2200        return s + " at {:#x}>".format(id(self))
2201
2202    def __str__(self):
2203        return self.format(0, self._keepws)
2204
2205    def _repr_pretty_(self, p, cycle):
2206        if cycle:
2207            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
2208        else:
2209            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
2210                p.breakable()
2211                p.text("name=")
2212                p.pretty(self.name)
2213                p.breakable()
2214                p.text("keepws=")
2215                p.pretty(self.keepws)
2216                if self.startdelim != "<?":
2217                    p.breakable()
2218                    p.text("startdelim=")
2219                    p.pretty(self.startdelim)
2220                if self.enddelim != "?>":
2221                    p.breakable()
2222                    p.text("enddelim=")
2223                    p.pretty(self.enddelim)
2224                for node in self.content:
2225                    p.breakable()
2226                    p.pretty(node)
2227                p.breakable()
2228                p.text("at {:#x}".format(id(self)))
2229
2230    def ul4ondump(self, encoder):
2231        encoder.dump(self.version)
2232        encoder.dump(self.source)
2233        encoder.dump(self.name)
2234        encoder.dump(self.keepws)
2235        encoder.dump(self.startdelim)
2236        encoder.dump(self.enddelim)
2237        encoder.dump(self.location)
2238        encoder.dump(self.endlocation)
2239        encoder.dump(self.content)
2240
2241    def ul4onload(self, decoder):
2242        version = decoder.load()
2243        if version != self.version:
2244            raise ValueError("invalid version, expected {!r}, got {!r}".format(self.version, version))
2245        self.source = decoder.load()
2246        self.name = decoder.load()
2247        self.keepws = decoder.load()
2248        self.startdelim = decoder.load()
2249        self.enddelim = decoder.load()
2250        self.location = decoder.load()
2251        self.endlocation = decoder.load()
2252        self.content = decoder.load()
2253
2254    def _get_keepws(self):
2255        return self._keepws
2256
2257    def _set_keepws(self, keepws):
2258        if bool(self._keepws) != bool(keepws):
2259            for node in self.iternodes():
2260                if isinstance(node, (Template, Function)):
2261                    node._keepws = keepws
2262                    node._pythonsource = node._pythonfunction = None
2263
2264    keepws = property(_get_keepws, _set_keepws)
2265
2266    @classmethod
2267    def loads(cls, data):
2268        """
2269        The class method :meth:`loads` loads the template/function from string
2270        :var:`data`. :var:`data` must contain the template/function in compiled
2271        UL4ON format.
2272        """
2273        from ll import ul4on
2274        return ul4on.loads(data)
2275
2276    @classmethod
2277    def load(cls, stream):
2278        """
2279        The class method :meth:`load` loads the template/function from the stream
2280        :var:`stream`. The stream must contain the template/function in compiled
2281        UL4ON format.
2282        """
2283        from ll import ul4on
2284        return ul4on.load(stream)
2285
2286    def dump(self, stream):
2287        """
2288        :meth:`dump` dumps the template/function in compiled UL4ON format to the
2289        stream :var:`stream`.
2290        """
2291        from ll import ul4on
2292        ul4on.dump(self, stream)
2293
2294    def dumps(self):
2295        """
2296        :meth:`dumps` returns the template/function in compiled UL4ON format
2297        (as a string).
2298        """
2299        from ll import ul4on
2300        return ul4on.dumps(self)
2301
2302    def _tokenize(self, source, startdelim, enddelim):
2303        """
2304        Tokenize the template/function source code :var:`source` into tags and
2305        non-tag text. :var:`startdelim` and :var:`enddelim` are used as the tag
2306        delimiters.
2307
2308        This is a generator which produces :class:`Location` objects for each tag
2309        or non-tag text. It will be called by :meth:`_compile` internally.
2310        """
2311        pattern = "{}(printx|print|code|for|if|elif|else|end|break|continue|render|template|function|return|note)(\s*((.|\\n)*?)\s*)?{}".format(re.escape(startdelim), re.escape(enddelim))
2312        pos = 0
2313        for match in re.finditer(pattern, source):
2314            if match.start() != pos:
2315                yield Location(source, None, pos, match.start(), pos, match.start())
2316            type = source[match.start(1):match.end(1)]
2317            if type != "note":
2318                yield Location(source, type, match.start(), match.end(), match.start(3), match.end(3))
2319            pos = match.end()
2320        end = len(source)
2321        if pos != end:
2322            yield Location(source, None, pos, end, pos, end)
2323
2324    def _parser(self, location, error):
2325        from ll import UL4Lexer, UL4Parser
2326        source = location.code
2327        if not source:
2328            raise ValueError(error)
2329        stream = antlr3.ANTLRStringStream(source)
2330        lexer = UL4Lexer.UL4Lexer(stream)
2331        lexer.location = location
2332        tokens = antlr3.CommonTokenStream(lexer)
2333        parser = UL4Parser.UL4Parser(tokens)
2334        parser.location = location
2335        return parser
2336
2337    def _compile(self, source, name, startdelim, enddelim):
2338        """
2339        Compile the template/function source code :var:`source` into an AST.
2340        :var:`startdelim` and :var:`enddelim` are used as the tag delimiters.
2341        """
2342        self.name = name
2343        self.startdelim = startdelim
2344        self.enddelim = enddelim
2345
2346        # This stack stores the nested for/if/elif/else/template/function blocks
2347        stack = [self]
2348        # This is a stack of the ``Template``/``Function`` objects
2349        callablestack = [self]
2350
2351        self.source = source
2352
2353        if source is None:
2354            return
2355
2356        def parseexpr(location):
2357            return self._parser(location, "expression required").expression()
2358
2359        def parsestmt(location):
2360            return self._parser(location, "statement required").statement()
2361
2362        def parsefor(location):
2363            return self._parser(location, "loop expression required").for_()
2364
2365        for location in self._tokenize(source, startdelim, enddelim):
2366            try:
2367                if location.type is None:
2368                    # Literal text will be ignored in functions
2369                    if isinstance(callablestack[-1], Template):
2370                        stack[-1].append(Text(location))
2371                elif location.type == "print":
2372                    stack[-1].append(Print(location, parseexpr(location)))
2373                elif location.type == "printx":
2374                    stack[-1].append(PrintX(location, parseexpr(location)))
2375                elif location.type == "code":
2376                    stack[-1].append(parsestmt(location))
2377                elif location.type == "if":
2378                    block = IfElIfElse(location, parseexpr(location))
2379                    stack[-1].append(block)
2380                    stack.append(block)
2381                elif location.type == "elif":
2382                    if not isinstance(stack[-1], IfElIfElse):
2383                        raise BlockError("elif doesn't match and if")
2384                    elif isinstance(stack[-1].content[-1], Else):
2385                        raise BlockError("else already seen in if")
2386                    stack[-1].newblock(ElIf(location, parseexpr(location)))
2387                elif location.type == "else":
2388                    if not isinstance(stack[-1], IfElIfElse):
2389                        raise BlockError("else doesn't match any if")
2390                    elif isinstance(stack[-1].content[-1], Else):
2391                        raise BlockError("else already seen in if")
2392                    stack[-1].newblock(Else(location))
2393                elif location.type == "end":
2394                    if len(stack) <= 1:
2395                        raise BlockError("not in any block")
2396                    code = location.code
2397                    if code:
2398                        if code == "if":
2399                            if not isinstance(stack[-1], IfElIfElse):
2400                                raise BlockError("endif doesn't match any if")
2401                        elif code == "for":
2402                            if not isinstance(stack[-1], For):
2403                                raise BlockError("endfor doesn't match any for")
2404                        elif code == "template":
2405                            if not isinstance(stack[-1], Template):
2406                                raise BlockError("endtemplate doesn't match any template")
2407                        elif code == "function":
2408                            if not isinstance(stack[-1], Function):
2409                                raise BlockError("endfunction doesn't match any function")
2410                        else:
2411                            raise BlockError("illegal end value {!r}".format(code))
2412                    last = stack.pop()
2413                    # Set ``endlocation`` of block
2414                    last.endlocation = location
2415                    if isinstance(last, IfElIfElse):
2416                        last.content[-1].endlocation = location
2417                    if isinstance(last, (Template, Function)):
2418                        callablestack.pop()
2419                elif location.type == "for":
2420                    block = parsefor(location)
2421                    stack[-1].append(block)
2422                    stack.append(block)
2423                elif location.type == "break":
2424                    for block in reversed(stack):
2425                        if isinstance(block, For):
2426                            break
2427                        elif isinstance(block, (Template, Function)):
2428                            raise BlockError("break outside of for loop")
2429                    stack[-1].append(Break(location))
2430                elif location.type == "continue":
2431                    for block in reversed(stack):
2432                        if isinstance(block, For):
2433                            break
2434                        elif isinstance(block, (Template, Function)):
2435                            raise BlockError("continue outside of for loop")
2436                    stack[-1].append(Continue(location))
2437                elif location.type == "render":
2438                    # ``<?render?>`` tags will be ignored inside functions
2439                    if isinstance(callablestack[-1], Template):
2440                        stack[-1].append(Render(location, parseexpr(location)))
2441                elif location.type == "template":
2442                    block = Template(None, location.code, self.keepws, self.startdelim, self.enddelim)
2443                    block.location = location # Set start ``location`` of sub template
2444                    block.source = self.source # The source of the top level template (so that the offsets in :class:`Location` are correct)
2445                    stack[-1].append(block)
2446                    stack.append(block)
2447                    callablestack.append(block)
2448                elif location.type == "function":
2449                    block = Function(None, location.code, self.keepws, self.startdelim, self.enddelim)
2450                    block.location = location # Set start ``location`` of function
2451                    block.source = self.source # The source of the top level template/function (so that the offsets in :class:`Location` are correct)
2452                    stack[-1].append(block)
2453                    stack.append(block)
2454                    callablestack.append(block)
2455                elif location.type == "return":
2456                    if isinstance(callablestack[-1], Template):
2457                        raise BlockError("return in template")
2458                    stack[-1].append(Return(location, parseexpr(location)))
2459                else: # Can't happen
2460                    raise ValueError("unknown tag {!r}".format(location.type))
2461            except Exception as exc:
2462                raise Error(location) from exc
2463        if len(stack) > 1:
2464            raise Error(stack[-1].location) from BlockError("block unclosed")
2465
2466    def _getast(self, astid):
2467        try:
2468            return self._astsbyid[astid]
2469        except KeyError:
2470            for node in self.iternodes():
2471                if id(node) == astid:
2472                    self._astsbyid[astid] = node
2473                    return node
2474            raise
2475
2476    def _handleexc(self, exc):
2477        source = [line.strip() for line in self._pythonsource.splitlines()]
2478        lineno = exc.__traceback__.tb_lineno-1
2479        for line in reversed(source[:lineno]):
2480            if line.startswith("#"):
2481                ast = self._getast(int(line.rpartition(" ")[2][1:-1])) # extract the id from the comment and fetch the appropriate node
2482                break
2483        else:
2484            raise # shouldn't happen -> reraise original
2485        if ast.location is None:
2486            raise # shouldn't happen, as a ``<?template?>``/``<?function?>`` tag itself can't result in any exceptions -> reraise original
2487        else:
2488            raise Error(ast.location) from exc
2489
2490    @classmethod
2491    def makefunction(cls, f):
2492        name = f.__name__
2493        if name.startswith("_"):
2494            name = name[1:]
2495        cls.functions[name] = f
2496        return f
2497
2498    @classmethod
2499    def makemethod(cls, f):
2500        name = f.__name__
2501        if name.startswith("_"):
2502            name = name[1:]
2503        cls.methods[name] = f
2504        return f
2505
2506
2507###
2508### Functions & methods
2509###
2510
2511@Code.makefunction
2512def _str(obj=""):
2513    if obj is None:
2514        return ""
2515    elif isinstance(obj, Undefined):
2516        return ""
2517    else:
2518        return str(obj)
2519
2520
2521@Code.makefunction
2522def _repr(obj):
2523    if isinstance(obj, str):
2524        return repr(obj)
2525    elif isinstance(obj, datetime.datetime):
2526        s = str(obj.isoformat())
2527        if s.endswith("T00:00:00"):
2528            s = s[:-9]
2529        return "@({})".format(s)
2530    elif isinstance(obj, datetime.date):
2531        return "@({})".format(obj.isoformat())
2532    elif isinstance(obj, datetime.timedelta):
2533        return repr(obj).partition(".")[-1]
2534    elif isinstance(obj, color.Color):
2535        if obj[3] == 0xff:
2536            s = "#{:02x}{:02x}{:02x}".format(obj[0], obj[1], obj[2])
2537            if s[1]==s[2] and s[3]==s[4] and s[5]==s[6]:
2538                return "#{}{}{}".format(s[1], s[3], s[5])
2539            return s
2540        else:
2541            s = "#{:02x}{:02x}{:02x}{:02x}".format(*obj)
2542            if s[1]==s[2] and s[3]==s[4] and s[5]==s[6] and s[7]==s[8]:
2543                return "#{}{}{}{}".format(s[1], s[3], s[5], s[7])
2544            return s
2545    elif isinstance(obj, collections.Sequence):
2546        return "[{}]".format(", ".join(_repr(item) for item in obj))
2547    elif isinstance(obj, collections.Mapping):
2548        return "{{{}}}".format(", ".join("{}: {}".format(_repr(key), _repr(value)) for (key, value) in obj.items()))
2549    else:
2550        return repr(obj)
2551
2552
2553@Code.makefunction
2554def _now():
2555    return datetime.datetime.now()
2556
2557
2558@Code.makefunction
2559def _utcnow():
2560    return datetime.datetime.utcnow()
2561
2562
2563@Code.makefunction
2564def _date(year, month, day, hour=0, minute=0, second=0, microsecond=0):
2565    return datetime.datetime(year, month, day, hour, minute, second, microsecond)
2566
2567
2568@Code.makefunction
2569def _timedelta(days=0, seconds=0, microseconds=0):
2570    return datetime.timedelta(days, seconds, microseconds)
2571
2572
2573@Code.makefunction
2574def _monthdelta(months=0):
2575    return misc.monthdelta(months)
2576
2577
2578@Code.makefunction
2579def _random():
2580    return random.random()
2581
2582
2583@Code.makefunction
2584def _xmlescape(obj):
2585    if obj is None:
2586        return ""
2587    elif isinstance(obj, Undefined):
2588        return ""
2589    else:
2590        return misc.xmlescape(str(obj))
2591
2592
2593@Code.makefunction
2594def _csv(obj):
2595    if obj is None:
2596        return ""
2597    elif isinstance(obj, Undefined):
2598        return ""
2599    elif not isinstance(obj, str):
2600        obj = _repr(obj)
2601    if any(c in obj for c in ',"\n'):
2602        return '"{}"'.format(obj.replace('"', '""'))
2603    return obj
2604
2605
2606@Code.makefunction
2607def _asjson(obj):
2608    if obj is None:
2609        return "null"
2610    elif isinstance(obj, Undefined):
2611        return "{}.undefined"
2612    if isinstance(obj, (bool, int, float, str)):
2613        return json.dumps(obj)
2614    elif isinstance(obj, datetime.datetime):
2615        return "new Date({}, {}, {}, {}, {}, {}, {})".format(obj.year, obj.month-1, obj.day, obj.hour, obj.minute, obj.second, obj.microsecond//1000)
2616    elif isinstance(obj, datetime.date):
2617        return "new Date({}, {}, {})".format(obj.year, obj.month-1, obj.day)
2618    elif isinstance(obj, datetime.timedelta):
2619        return "ul4.TimeDelta.create({}, {}, {})".format(obj.days, obj.seconds, obj.microseconds)
2620    elif isinstance(obj, misc.monthdelta):
2621        return "ul4.MonthDelta.create({})".format(obj.months)
2622    elif isinstance(obj, color.Color):
2623        return "ul4.Color.create({}, {}, {}, {})".format(*obj)
2624    elif isinstance(obj, collections.Mapping):
2625        return "{{{}}}".format(", ".join("{}: {}".format(_asjson(key), _asjson(value)) for (key, value) in obj.items()))
2626    elif isinstance(obj, collections.Sequence):
2627        return "[{}]".format(", ".join(_asjson(item) for item in obj))
2628    elif isinstance(obj, Template):
2629        return obj.jssource()
2630    else:
2631        raise TypeError("can't handle object of type {}".format(type(obj)))
2632
2633
2634@Code.makefunction
2635def _fromjson(string):
2636    from ll import ul4on
2637    return json.loads(string)
2638
2639
2640@Code.makefunction
2641def _asul4on(obj):
2642    from ll import ul4on
2643    return ul4on.dumps(obj)
2644
2645
2646@Code.makefunction
2647def _fromul4on(string):
2648    from ll import ul4on
2649    return ul4on.loads(string)
2650
2651
2652@Code.makefunction
2653def _int(obj=0, base=None):
2654    if base is None:
2655        return int(obj)
2656    else:
2657        return int(obj, base)
2658
2659
2660@Code.makefunction
2661def _float(obj=0.0):
2662    return float(obj)
2663
2664
2665@Code.makefunction
2666def _bool(obj=False):
2667    return bool(obj)
2668
2669
2670@Code.makefunction
2671def _len(sequence):
2672    return len(sequence)
2673
2674
2675@Code.makefunction
2676def _abs(number):
2677    return abs(number)
2678
2679
2680@Code.makefunction
2681def _any(iterable):
2682    return any(iterable)
2683
2684
2685@Code.makefunction
2686def _all(iterable):
2687    return all(iterable)
2688
2689
2690@Code.makefunction
2691def _enumerate(iterable, start=0):
2692    return enumerate(iterable, start)
2693
2694
2695@Code.makefunction
2696def _enumfl(iterable, start=0):
2697    lastitem = None
2698    first = True
2699    i = start
2700    it = iter(iterable)
2701    try:
2702        item = next(it)
2703    except StopIteration:
2704        return
2705    while True:
2706        try:
2707            (lastitem, item) = (item, next(it))
2708        except StopIteration:
2709            yield (i, first, True, item) # Items haven't been swapped yet
2710            return
2711        else:
2712            yield (i, first, False, lastitem)
2713            first = False
2714        i += 1
2715
2716
2717@Code.makefunction
2718def _isfirstlast(iterable):
2719    lastitem = None
2720    first = True
2721    it = iter(iterable)
2722    try:
2723        item = next(it)
2724    except StopIteration:
2725        return
2726    while True:
2727        try:
2728            (lastitem, item) = (item, next(it))
2729        except StopIteration:
2730            yield (first, True, item) # Items haven't been swapped yet
2731            return
2732        else:
2733            yield (first, False, lastitem)
2734            first = False
2735
2736
2737@Code.makefunction
2738def _isfirst(iterable):
2739    first = True
2740    for item in iterable:
2741        yield (first, item)
2742        first = False
2743
2744
2745@Code.makefunction
2746def _islast(iterable):
2747    lastitem = None
2748    it = iter(iterable)
2749    try:
2750        item = next(it)
2751    except StopIteration:
2752        return
2753    while True:
2754        try:
2755            (lastitem, item) = (item, next(it))
2756        except StopIteration:
2757            yield (True, item) # Items haven't been swapped yet
2758            return
2759        else:
2760            yield (False, lastitem)
2761
2762
2763@Code.makefunction
2764def _isundefined(obj):
2765    return isinstance(obj, Undefined)
2766
2767
2768@Code.makefunction
2769def _isdefined(obj):
2770    return not isinstance(obj, Undefined)
2771
2772
2773@Code.makefunction
2774def _isnone(obj):
2775    return obj is None
2776
2777
2778@Code.makefunction
2779def _isstr(obj):
2780    return isinstance(obj, str)
2781
2782
2783@Code.makefunction
2784def _isint(obj):
2785    return isinstance(obj, int) and not isinstance(obj, bool)
2786
2787
2788@Code.makefunction
2789def _isfloat(obj):
2790    return isinstance(obj, float)
2791
2792
2793@Code.makefunction
2794def _isbool(obj):
2795    return isinstance(obj, bool)
2796
2797
2798@Code.makefunction
2799def _isdate(obj):
2800    return isinstance(obj, (datetime.datetime, datetime.date))
2801
2802
2803@Code.makefunction
2804def _istimedelta(obj):
2805    return isinstance(obj, datetime.timedelta)
2806
2807
2808@Code.makefunction
2809def _ismonthdelta(obj):
2810    return isinstance(obj, misc.monthdelta)
2811
2812
2813@Code.makefunction
2814def _islist(obj):
2815    return isinstance(obj, collections.Sequence) and not isinstance(obj, str) and not isinstance(obj, color.Color)
2816
2817
2818@Code.makefunction
2819def _isdict(obj):
2820    return isinstance(obj, collections.Mapping) and not isinstance(obj, Template)
2821
2822
2823@Code.makefunction
2824def _iscolor(obj):
2825    return isinstance(obj, color.Color)
2826
2827
2828@Code.makefunction
2829def _istemplate(obj):
2830    return isinstance(obj, (Template, TemplateClosure))
2831
2832
2833@Code.makefunction
2834def _isfunction(obj):
2835    return callable(obj)
2836
2837
2838@Code.makefunction
2839def _chr(i):
2840    return chr(i)
2841
2842
2843@Code.makefunction
2844def _ord(c):
2845    return ord(c)
2846
2847
2848@Code.makefunction
2849def _hex(number):
2850    return hex(number)
2851
2852
2853@Code.makefunction
2854def _oct(number):
2855    return oct(number)
2856
2857
2858@Code.makefunction
2859def _bin(number):
2860    return bin(number)
2861
2862
2863@Code.makefunction
2864def _min(*args):
2865    return min(*args)
2866
2867
2868@Code.makefunction
2869def _max(*args):
2870    return max(*args)
2871
2872
2873@Code.makefunction
2874def _sorted(iterable):
2875    return sorted(iterable)
2876
2877
2878@Code.makefunction
2879def _range(*args):
2880    return range(*args)
2881
2882
2883@Code.makefunction
2884def _type(obj):
2885    if obj is None:
2886        return "none"
2887    elif isinstance(obj, Undefined):
2888        return "undefined"
2889    elif isinstance(obj, str):
2890        return "str"
2891    elif isinstance(obj, bool):
2892        return "bool"
2893    elif isinstance(obj, int):
2894        return "int"
2895    elif isinstance(obj, float):
2896        return "float"
2897    elif isinstance(obj, (datetime.datetime, datetime.date)):
2898        return "date"
2899    elif isinstance(obj, datetime.timedelta):
2900        return "timedelta"
2901    elif isinstance(obj, misc.monthdelta):
2902        return "monthdelta"
2903    elif isinstance(obj, color.Color):
2904        return "color"
2905    elif isinstance(obj, (Template, TemplateClosure)):
2906        return "template"
2907    elif isinstance(obj, collections.Mapping):
2908        return "dict"
2909    elif isinstance(obj, color.Color):
2910        return "color"
2911    elif isinstance(obj, collections.Sequence):
2912        return "list"
2913    elif callable(obj):
2914        return "function"
2915    return None
2916
2917
2918@Code.makefunction
2919def _reversed(sequence):
2920    return reversed(sequence)
2921
2922
2923@Code.makefunction
2924def _randrange(*args):
2925    return random.randrange(*args)
2926
2927
2928@Code.makefunction
2929def _randchoice(sequence):
2930    return random.choice(sequence)
2931
2932
2933@Code.makefunction
2934def _format(obj, fmt, lang=None):
2935    if isinstance(obj, (datetime.date, datetime.time, datetime.timedelta)):
2936        if lang is None:
2937            lang = "en"
2938        oldlocale = locale.getlocale()
2939        try:
2940            for candidate in (locale.normalize(lang), locale.normalize("en"), ""):
2941                try:
2942                    locale.setlocale(locale.LC_ALL, candidate)
2943                    return format(obj, fmt)
2944                except locale.Error:
2945                    if not candidate:
2946                        return format(obj, fmt)
2947        finally:
2948            try:
2949                locale.setlocale(locale.LC_ALL, oldlocale)
2950            except locale.Error:
2951                pass
2952    else:
2953        return format(obj, fmt)
2954
2955
2956@Code.makefunction
2957def _zip(*iterables):
2958    return zip(*iterables)
2959
2960
2961@Code.makefunction
2962def _urlquote(string):
2963    return urlparse.quote_plus(string)
2964
2965
2966@Code.makefunction
2967def _urlunquote(string):
2968    return urlparse.unquote_plus(string)
2969
2970
2971@Code.makefunction
2972def _rgb(r, g, b, a=1.0):
2973    return color.Color.fromrgb(r, g, b, a)
2974
2975
2976@Code.makefunction
2977def _hls(h, l, s, a=1.0):
2978    return color.Color.fromhls(h, l, s, a)
2979
2980
2981@Code.makefunction
2982def _hsv(h, s, v, a=1.0):
2983    return color.Color.fromhsv(h, s, v, a)
2984
2985
2986@Code.makemethod
2987def _split(obj, sep=None, count=None):
2988    return obj.split(sep, count if count is not None else -1)
2989
2990
2991@Code.makemethod
2992def _rsplit(obj, sep=None, count=None):
2993    return obj.rsplit(sep, count if count is not None else -1)
2994
2995
2996@Code.makemethod
2997def _strip(obj, chars=None):
2998    return obj.strip(chars)
2999
3000
3001@Code.makemethod
3002def _lstrip(obj, chars=None):
3003    return obj.lstrip(chars)
3004
3005
3006@Code.makemethod
3007def _rstrip(obj, chars=None):
3008    return obj.rstrip(chars)
3009
3010
3011@Code.makemethod
3012def _find(obj, sub, start=None, end=None):
3013    if isinstance(obj, str):
3014        return obj.find(sub, start, end)
3015    else:
3016        try:
3017            if end is None:
3018                if start is None:
3019                    return obj.index(sub)
3020                return obj.index(sub, start)
3021            return obj.index(sub, start, end)
3022        except ValueError:
3023            return -1
3024
3025
3026@Code.makemethod
3027def _rfind(obj, sub, start=None, end=None):
3028    if isinstance(obj, str):
3029        return obj.rfind(sub, start, end)
3030    else:
3031        for i in reversed(range(*slice(start, end).indices(len(obj)))):
3032            if obj[i] == sub:
3033                return i
3034        return -1
3035
3036
3037@Code.makemethod
3038def _startswith(obj, prefix):
3039    return obj.startswith(prefix)
3040
3041
3042@Code.makemethod
3043def _endswith(obj, suffix):
3044    return obj.endswith(suffix)
3045
3046
3047@Code.makemethod
3048def _upper(obj):
3049    return obj.upper()
3050
3051
3052@Code.makemethod
3053def _lower(obj):
3054    return obj.lower()
3055
3056
3057@Code.makemethod
3058def _capitalize(obj):
3059    return obj.capitalize()
3060
3061
3062@Code.makemethod
3063def _replace(obj, old, new, count=None):
3064    if count is None:
3065        return obj.replace(old, new)
3066    else:
3067        return obj.replace(old, new, count)
3068
3069
3070@Code.makemethod
3071def _r(obj):
3072    return obj.r()
3073
3074
3075@Code.makemethod
3076def _g(obj):
3077    return obj.g()
3078
3079
3080@Code.makemethod
3081def _b(obj):
3082    return obj.b()
3083
3084
3085@Code.makemethod
3086def _a(obj):
3087    return obj.a()
3088
3089
3090@Code.makemethod
3091def _hls(obj):
3092    return obj.hls()
3093
3094
3095@Code.makemethod
3096def _hlsa(obj):
3097    return obj.hlsa()
3098
3099
3100@Code.makemethod
3101def _hsv(obj):
3102    return obj.hsv()
3103
3104
3105@Code.makemethod
3106def _hsva(obj):
3107    return obj.hsva()
3108
3109
3110@Code.makemethod
3111def _lum(obj):
3112    return obj.lum()
3113
3114
3115@Code.makemethod
3116def _weekday(obj):
3117    return obj.weekday()
3118
3119
3120@Code.makemethod
3121def _week(obj, firstweekday=None):
3122    if firstweekday is None:
3123        firstweekday = 0
3124    else:
3125        firstweekday %= 7
3126    jan1 = obj.__class__(obj.year, 1, 1)
3127    yearday = (obj - jan1).days+7
3128    jan1weekday = jan1.weekday()
3129    while jan1weekday != firstweekday:
3130        yearday -= 1
3131        jan1weekday += 1
3132        if jan1weekday == 7:
3133            jan1weekday = 0
3134    return yearday//7
3135
3136
3137@Code.makemethod
3138def _items(obj):
3139    return obj.items()
3140
3141
3142@Code.makemethod
3143def _values(obj):
3144    return obj.values()
3145
3146
3147@Code.makemethod
3148def _join(obj, iterable):
3149    return obj.join(iterable)
3150
3151
3152@Code.makemethod
3153def _render(obj, **vars):
3154    return obj.render(**vars)
3155
3156
3157@Code.makemethod
3158def _renders(obj, **vars):
3159    return obj.renders(**vars)
3160
3161
3162@Code.makemethod
3163def _mimeformat(obj):
3164    weekdayname = ("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
3165    monthname = (None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec")
3166    return "{1}, {0.day:02d} {2:3} {0.year:4} {0.hour:02}:{0.minute:02}:{0.second:02} GMT".format(obj, weekdayname[obj.weekday()], monthname[obj.month])
3167
3168
3169@Code.makemethod
3170def _isoformat(obj):
3171    result = obj.isoformat()
3172    suffix = "T00:00:00"
3173    if result.endswith(suffix):
3174        return result[:-len(suffix)]
3175    return result
3176
3177
3178@Code.makemethod
3179def _yearday(obj):
3180    return (obj - obj.__class__(obj.year, 1, 1)).days+1
3181
3182
3183@Code.makemethod
3184def _get(obj, key, default=None):
3185    return obj.get(key, default)
3186
3187
3188@Code.makemethod
3189def _withlum(obj, lum):
3190    return obj.withlum(lum)
3191
3192
3193@Code.makemethod
3194def _witha(obj, a):
3195    return obj.witha(a)
3196
3197
3198@Code.makemethod
3199def _day(obj):
3200    return obj.day
3201
3202
3203@Code.makemethod
3204def _month(obj):
3205    return obj.month
3206
3207
3208@Code.makemethod
3209def _year(obj):
3210    return obj.year
3211
3212
3213@Code.makemethod
3214def _hour(obj):
3215    return obj.hour
3216
3217
3218@Code.makemethod
3219def _minute(obj):
3220    return obj.minute
3221
3222
3223@Code.makemethod
3224def _second(obj):
3225    return obj.second
3226
3227
3228@Code.makemethod
3229def _microsecond(obj):
3230    return obj.microsecond
3231
3232
3233@register("template")
3234class Template(Code):
3235    """
3236    A template object is normally created by passing the template source to the
3237    constructor. It can also be loaded from the compiled format via the class
3238    methods :meth:`load` (from a stream) or :meth:`loads` (from a string).
3239
3240    The compiled format can be generated with the methods :meth:`dump` (which
3241    dumps the format to a stream) or :meth:`dumps` (which returns a string with
3242    the compiled format).
3243
3244    Rendering the template can be done with the methods :meth:`render` (which
3245    is a generator) or :meth:`renders` (which returns a string).
3246
3247    A :class:`Template` object is itself an AST node. Evaluating it will store
3248    the template object under its name in the local variables.
3249    """
3250
3251    def format(self, indent, keepws):
3252        v = []
3253        name = self.name if self.name is not None else "unnamed"
3254        v.append("{}template {}()\n".format(indent*"\t", name))
3255        v.append("{}{{\n".format(indent*"\t"))
3256        indent += 1
3257        for node in self.content:
3258            v.append(node.format(indent, keepws))
3259        indent -= 1
3260        v.append("{}}}\n".format(indent*"\t"))
3261        return "".join(v)
3262
3263    def formatpython(self, indent, keepws):
3264        return "{i}# <?template?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] = ul4c.TemplateClosure(self._getast({id}), vars)\n".format(i=indent*"\t", n=self.name if self.name is not None else "unnamed", id=id(self), l=self.location)
3265
3266    def render(self, **vars):
3267        """
3268        Render the template iteratively (i.e. this is a generator).
3269        :var:`vars` contains the top level variables available to the
3270        template code.
3271        """
3272        return self.pythonfunction()(self, vars)
3273
3274    def renders(self, **vars):
3275        """
3276        Render the template as a string. :var:`vars` contains the top level
3277        variables available to the template code.
3278        """
3279        return "".join(self.pythonfunction()(self, vars))
3280
3281    def pythonfunction(self):
3282        """
3283        Return a Python generator that can be called to render the template.
3284        """
3285        if self._pythonfunction is None:
3286            name = self.name if self.name is not None else "unnamed"
3287            source = self.pythonsource()
3288            ns = {}
3289            exec(source, ns)
3290            self._pythonfunction = ns[name]
3291        return self._pythonfunction
3292
3293    def pythonsource(self):
3294        """
3295        Return the template as Python source code.
3296        """
3297        if self._pythonsource is None:
3298            v = []
3299            v.append("def {}(self, vars):\n".format(self.name if self.name is not None else "unnamed"))
3300            v.append("\timport datetime, collections\n")
3301            v.append("\tfrom ll import ul4c, misc, color\n")
3302            v.append("\tif 0:\n")
3303            v.append("\t\tyield\n")
3304            v.append("\ttry:\n")
3305            v.append("\t\tallvars = ul4c._AllVars(vars, self.functions)\n")
3306            for node in self.content:
3307                v.append(node.formatpython(2, self._keepws))
3308            v.append("\texcept Exception as exc:\n")
3309            v.append("\t\tself._handleexc(exc)\n")
3310            self._pythonsource = "".join(v)
3311        return self._pythonsource
3312
3313    def jssource(self):
3314        """
3315        Return the template as the source code of a Javascript function.
3316        """
3317        return "ul4.Template.loads({})".format(_asjson(self.dumps()))
3318
3319    def javasource(self):
3320        """
3321        Return the template as Java source code.
3322        """
3323        return "com.livinglogic.ul4.InterpretedTemplate.loads({})".format(misc.javaexpr(self.dumps()))
3324
3325
3326class TemplateClosure(Object):
3327    fields = {"location", "endlocation", "name", "source", "startdelim", "enddelim", "content"}
3328
3329    def __init__(self, template, vars):
3330        self.template = template
3331        # Freeze variables of the currently running templates/functions
3332        self.vars = vars.copy()
3333
3334    def render(self, **vars):
3335        return self.template.render(**collections.ChainMap(vars, self.vars))
3336
3337    def renders(self, **vars):
3338        return self.template.renders(**collections.ChainMap(vars, self.vars))
3339
3340    def __getattr__(self, name):
3341        return getattr(self.template, name)
3342
3343    def __repr__(self):
3344        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} name={0.name!r} keepws={0.keepws!r}".format(self)
3345        if self.startdelim != "<?":
3346            s += " startdelim={0.startdelim!r}".format(self)
3347        if self.enddelim != "?>":
3348            s += " enddelim={0.enddelim!r}".format(self)
3349        if self.content:
3350            s + " ..."
3351        return s + " at {:#x}>".format(id(self))
3352
3353    def _repr_pretty_(self, p, cycle):
3354        if cycle:
3355            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
3356        else:
3357            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
3358                p.breakable()
3359                p.text("name=")
3360                p.pretty(self.name)
3361                p.breakable()
3362                p.text("keepws=")
3363                p.pretty(self.keepws)
3364                if self.startdelim != "<?":
3365                    p.breakable()
3366                    p.text("startdelim=")
3367                    p.pretty(self.startdelim)
3368                if self.enddelim != "?>":
3369                    p.breakable()
3370                    p.text("enddelim=")
3371                    p.pretty(self.enddelim)
3372                for node in self.content:
3373                    p.breakable()
3374                    p.pretty(node)
3375                p.breakable()
3376                p.text("at {:#x}".format(id(self)))
3377
3378
3379@register("function")
3380class Function(Code):
3381    """
3382    A function object is normally created by passing the function source to the
3383    constructor. It can also be loaded from the compiled format via the class
3384    methods :meth:`load` (from a stream) or :meth:`loads` (from a string).
3385
3386    The compiled format can be generated with the methods :meth:`dump` (which
3387    dumps the format to a stream) or :meth:`dumps` (which returns a string with
3388    the compiled format).
3389
3390    A :class:`Function` object can be called like a normal Python function.
3391
3392    A :class:`Function` object is itself an AST node. Evaluating it will store
3393    the function object under its name in the local variables.
3394    """
3395    def format(self, indent, keepws):
3396        v = []
3397        name = self.name if self.name is not None else "unnamed"
3398        v.append("{}function {}()\n".format(indent*"\t", name))
3399        v.append("{}{{\n".format(indent*"\t"))
3400        indent += 1
3401        for node in self.content:
3402            v.append(node.format(indent, keepws))
3403        indent -= 1
3404        v.append("{}}}\n".format(indent*"\t"))
3405        return "".join(v)
3406
3407    def formatpython(self, indent, keepws):
3408        return "{i}# <?function?> tag at position {l.starttag}:{l.endtag} ({id})\n{i}vars[{n!r}] = ul4c.FunctionClosure(self._getast({id}), vars)\n".format(i=indent*"\t", n=self.name if self.name is not None else "unnamed", id=id(self), l=self.location)
3409
3410    def __call__(self, **vars):
3411        """
3412        Call the function and return the resulting value. :var:`vars` contains
3413        the top level variables available to the function code.
3414        """
3415        return self.pythonfunction()(self, vars)
3416
3417    def pythonfunction(self):
3418        """
3419        Return a Python function that can be called to execute the UL4 function.
3420        """
3421        if self._pythonfunction is None:
3422            name = self.name if self.name is not None else "unnamed"
3423            source = self.pythonsource()
3424            ns = {}
3425            exec(source, ns)
3426            self._pythonfunction = ns[name]
3427        return self._pythonfunction
3428
3429    def pythonsource(self):
3430        """
3431        Return the function as Python source code.
3432        """
3433        if self._pythonsource is None:
3434            v = []
3435            v.append("def {}(self, vars):\n".format(self.name if self.name is not None else "unnamed"))
3436            v.append("\timport datetime, collections\n")
3437            v.append("\tfrom ll import ul4c, misc, color\n")
3438            v.append("\ttry:\n")
3439            v.append("\t\tallvars = ul4c._AllVars(vars, self.functions)\n")
3440            for node in self.content:
3441                v.append(node.formatpython(2, self._keepws))
3442            v.append("\texcept Exception as exc:\n")
3443            v.append("\t\tself._handleexc(exc)\n")
3444            self._pythonsource = "".join(v)
3445        return self._pythonsource
3446
3447    def jssource(self):
3448        """
3449        Return the function as the source code of a Javascript function.
3450        """
3451        return "ul4.Function.loads({})".format(_asjson(self.dumps()))
3452
3453    def javasource(self):
3454        """
3455        Return the function as Java source code.
3456        """
3457        return "com.livinglogic.ul4.InterpretedFunction.loads({})".format(misc.javaexpr(self.dumps()))
3458
3459
3460class FunctionClosure(Object):
3461    fields = {"location", "endlocation", "name", "source", "startdelim", "enddelim", "content"}
3462
3463    def __init__(self, function, vars):
3464        self.function = function
3465        # Freeze variables of the currently running templates/functions
3466        self.vars = vars.copy()
3467
3468    def __call__(self, **vars):
3469        return self.function(**collections.ChainMap(vars, self.vars))
3470
3471    def __getattr__(self, name):
3472        return getattr(self.function, name)
3473
3474    def __repr__(self):
3475        s = "<{0.__class__.__module__}.{0.__class__.__qualname__} name={0.name!r} keepws={0.keepws!r}".format(self)
3476        if self.startdelim != "<?":
3477            s += " startdelim={0.startdelim!r}".format(self)
3478        if self.enddelim != "?>":
3479            s += " enddelim={0.enddelim!r}".format(self)
3480        if self.content:
3481            s + " ..."
3482        return s + " at {:#x}>".format(id(self))
3483
3484    def _repr_pretty_(self, p, cycle):
3485        if cycle:
3486            p.text("<{0.__class__.__module__}.{0.__class__.__qualname__} ... at {1:#x}>".format(self, id(self)))
3487        else:
3488            with p.group(4, "<{0.__class__.__module__}.{0.__class__.__qualname__}".format(self), ">"):
3489                p.breakable()
3490                p.text("name=")
3491                p.pretty(self.name)
3492                p.breakable()
3493                p.text("keepws=")
3494                p.pretty(self.keepws)
3495                if self.startdelim != "<?":
3496                    p.breakable()
3497                    p.text("startdelim=")
3498                    p.pretty(self.startdelim)
3499                if self.enddelim != "?>":
3500                    p.breakable()
3501                    p.text("enddelim=")
3502                    p.pretty(self.enddelim)
3503                for node in self.content:
3504                    p.breakable()
3505                    p.pretty(node)
3506                p.breakable()
3507                p.text("at {:#x}".format(id(self)))
3508
3509
3510###
3511### Helper classes/functions used at runtime
3512###
3513
3514class _AllVars(collections.ChainMap):
3515    def __setitem__(self, key, value):
3516        if key == "self":
3517            raise ValueError("can't assign to self")
3518        collections.ChainMap.__setitem__(self, key, value)
3519
3520
3521def _makedict(*items):
3522    result = {}
3523    for item in items:
3524        if len(item) == 1:
3525            result.update(item[0])
3526        else:
3527            result[item[0]] = item[1]
3528    return result
3529
3530
3531def _formatnestednameul4(name):
3532    if isinstance(name, str):
3533        return name
3534    elif len(name) == 1:
3535        return "({},)".format(_formatnestednameul4(name[0]))
3536    else:
3537        return "({})".format(", ".join(_formatnestednameul4(name) for name in name))
3538
3539
3540def _formatnestednamepython(name):
3541    if isinstance(name, str):
3542        return "allvars[{!r}]".format(name)
3543    elif len(name) == 1:
3544        return "({},)".format(_formatnestednamepython(name[0]))
3545    else:
3546        return "({})".format(", ".join(_formatnestednamepython(name) for name in name))
3547
3548
3549def _getitem(container, key):
3550    """
3551    Helper for the ``getitem`` operator.
3552    """
3553    try:
3554        return container[key]
3555    except KeyError:
3556        return UndefinedKey(key)
3557    except IndexError:
3558        return UndefinedIndex(key)
Note: See TracBrowser for help on using the browser.