root/livinglogic.java.ul4/library/src/com/livinglogic/ul4/InterpretedTemplate.java @ 204:1e475c942353

Revision 204:1e475c942353, 69.7 KB (checked in by Walter Doerwald <walter@…>, 10 years ago)

Add support for the render method to both the interpreted and the JSP template.

Line 
1package com.livinglogic.ul4;
2
3import java.io.BufferedReader;
4import java.io.IOException;
5import java.io.Reader;
6import java.io.Writer;
7import java.io.StringWriter;
8import java.io.StringReader;
9import java.util.LinkedList;
10import java.util.List;
11import java.util.HashMap;
12import java.util.Iterator;
13import java.util.Locale;
14import java.util.Map;
15import java.util.Date;
16import java.util.ArrayList;
17import java.util.regex.Pattern;
18import java.util.regex.Matcher;
19import javax.servlet.*;
20import javax.servlet.http.*;
21import javax.servlet.jsp.*;
22import org.apache.commons.lang.ObjectUtils;
23
24public class InterpretedTemplate implements Template
25{
26    // used by the code tokenizer
27    private static Pattern tokenPattern;
28    private static Pattern namePattern;
29    private static Pattern floatPattern;
30    private static Pattern hexintPattern;
31    private static Pattern octintPattern;
32    private static Pattern binintPattern;
33    private static Pattern intPattern;
34    private static Pattern datePattern;
35    private static Pattern color3Pattern;
36    private static Pattern color4Pattern;
37    private static Pattern color6Pattern;
38    private static Pattern color8Pattern;
39    private static Pattern whitespacePattern;
40    private static Pattern escaped8BitCharPattern;
41    private static Pattern escaped16BitCharPattern;
42    private static Pattern escaped32BitCharPattern;
43
44    static
45    {
46        // Initializes regular expressions
47        tokenPattern = Pattern.compile("\\(|\\)|\\[|\\]|\\{|\\}|\\.|,|==|\\!=|<=|<|>=|>|=|\\+=|\\-=|\\*=|/=|//=|%=|%|:|\\+|-|\\*\\*|\\*|//|/");
48        namePattern = Pattern.compile("[a-zA-Z_][\\w]*");
49        // We don't have negatve numbers, this is handled by constant folding in the AST for unary minus
50        floatPattern = Pattern.compile("(\\d+(\\.\\d*)?[eE][+-]?\\d+|\\d+\\.\\d*([eE][+-]?\\d+)?)");
51        hexintPattern = Pattern.compile("0[xX][\\da-fA-F]+");
52        octintPattern = Pattern.compile("0[oO][0-7]+");
53        binintPattern = Pattern.compile("0[bB][01]+");
54        intPattern = Pattern.compile("\\d+");
55        datePattern = Pattern.compile("\\d{4}-\\d{2}-\\d{2}T(\\d{2}:\\d{2}(:\\d{2}(.\\d{6})?)?)?");
56        color3Pattern = Pattern.compile("[#][0-9a-zA-Z]{3}");
57        color4Pattern = Pattern.compile("[#][0-9a-zA-Z]{4}");
58        color6Pattern = Pattern.compile("[#][0-9a-zA-Z]{6}");
59        color8Pattern = Pattern.compile("[#][0-9a-zA-Z]{8}");
60        whitespacePattern = Pattern.compile("\\s+");
61        escaped8BitCharPattern = Pattern.compile("\\\\x[0-9a-fA-F]{2}");
62        escaped16BitCharPattern = Pattern.compile("\\\\u[0-9a-fA-F]{4}");
63        escaped32BitCharPattern = Pattern.compile("\\\\U[0-9a-fA-F]{8}");
64    }
65
66    /**
67     * Contains information about the currently running loops during rendering
68     * of the template.
69     */
70    class IteratorStackEntry
71    {
72        /**
73         * The register number where the loop variable has to be stored for each
74         * run through the loop body.
75         */
76        public int iteratorRegSpec;
77
78        /**
79         * The program counter (i.e. the index of the opcode in the
80         * {@link #opcodes} list) where the loop started (i.e. the location of the
81         * <code>for</code> opcode).
82         */
83        public int pcFor;
84
85        /**
86         * The program counter (i.e. the index of the opcode in the
87         * {@link #opcodes} list) where the loop ends (i.e. the location of the
88         * <code>endfor</code> opcode).
89         */
90        public int pcEndFor;
91
92        /**
93         * The iterator producing the values for the loop variable.
94         */
95        public Iterator iterator;
96
97        public IteratorStackEntry(int iteratorRegSpec, int pcFor, int pcEndFor, Iterator iterator)
98        {
99            this.iteratorRegSpec = iteratorRegSpec;
100            this.pcFor = pcFor;
101            this.pcEndFor = pcEndFor;
102            this.iterator = iterator;
103        }
104    }
105
106    /**
107     * The header used in the compiled format of the template.
108     */
109    public static final String HEADER = "ul4";
110
111    /**
112     * The version number used in the compiled format of the template.
113     */
114    public static final String VERSION = "8";
115
116    /**
117     * The start delimiter for tags (defaults to <code>&lt;?</code>)
118     */ 
119    public String startdelim;
120
121    /**
122     * The end delimiter for tags (defaults to <code>?&gt;</code>)
123     */ 
124    public String enddelim;
125
126    /**
127     * The template source.
128     */ 
129    public String source;
130
131    /**
132     * The list of opcodes.
133     */ 
134    public List opcodes;
135   
136    /**
137     * The locale to be used when formatting int, float or date objects.
138     */ 
139    public Locale defaultLocale;
140
141    /**
142     * Has {@link annotate} been called for this template?
143     */
144    private boolean annotated = false;
145
146    /**
147     * Creates an empty template object.
148     */
149    public InterpretedTemplate()
150    {
151        this.source = null;
152        this.opcodes = new LinkedList();
153        this.defaultLocale = Locale.GERMANY;
154    }
155
156    /**
157     * Appends a new opcode to {@link opcodes}.
158     */
159    public void opcode(int name, Location location)
160    {
161        opcodes.add(new Opcode(name, -1, -1, -1, -1, -1, null, location));
162    }
163
164    /**
165     * Appends a new opcode to {@link opcodes}.
166     */
167    public void opcode(int name, String arg, Location location)
168    {
169        opcodes.add(new Opcode(name, -1, -1, -1, -1, -1, arg, location));
170    }
171
172    /**
173     * Appends a new opcode to {@link opcodes}.
174     */
175    public void opcode(int name, int r1, Location location)
176    {
177        opcodes.add(new Opcode(name, r1, -1, -1, -1, -1, null, location));
178    }
179
180    /**
181     * Appends a new opcode to {@link opcodes}.
182     */
183    public void opcode(int name, int r1, String arg, Location location)
184    {
185        opcodes.add(new Opcode(name, r1, -1, -1, -1, -1, arg, location));
186    }
187
188    /**
189     * Appends a new opcode to {@link opcodes}.
190     */
191    public void opcode(int name, int r1, int r2, Location location)
192    {
193        opcodes.add(new Opcode(name, r1, r2, -1, -1, -1, null, location));
194    }
195
196    /**
197     * Appends a new opcode to {@link opcodes}.
198     */
199    public void opcode(int name, int r1, int r2, String arg, Location location)
200    {
201        opcodes.add(new Opcode(name, r1, r2, -1, -1, -1, arg, location));
202    }
203
204    /**
205     * Appends a new opcode to {@link opcodes}.
206     */
207    public void opcode(int name, int r1, int r2, int r3, Location location)
208    {
209        opcodes.add(new Opcode(name, r1, r2, r3, -1, -1, null, location));
210    }
211
212    /**
213     * Appends a new opcode to {@link opcodes}.
214     */
215    public void opcode(int name, int r1, int r2, int r3, String arg, Location location)
216    {
217        opcodes.add(new Opcode(name, r1, r2, r3, -1, -1, arg, location));
218    }
219
220    /**
221     * Appends a new opcode to {@link opcodes}.
222     */
223    public void opcode(int name, int r1, int r2, int r3, int r4, Location location)
224    {
225        opcodes.add(new Opcode(name, r1, r2, r3, r4, -1, null, location));
226    }
227
228    /**
229     * Appends a new opcode to {@link opcodes}.
230     */
231    public void opcode(int name, int r1, int r2, int r3, int r4, String arg, Location location)
232    {
233        opcodes.add(new Opcode(name, r1, r2, r3, r4, -1, arg, location));
234    }
235
236    /**
237     * Appends a new opcode to {@link opcodes}.
238     */
239    public void opcode(int name, int r1, int r2, int r3, int r4, int r5, Location location)
240    {
241        opcodes.add(new Opcode(name, r1, r2, r3, r4, r5, null, location));
242    }
243
244    /**
245     * Appends a new opcode to {@link opcodes}.
246     */
247    public void opcode(int name, int r1, int r2, int r3, int r4, int r5, String arg, Location location)
248    {
249        opcodes.add(new Opcode(name, r1, r2, r3, r4, r5, arg, location));
250    }
251
252    protected static int readintInternal(Reader reader, char terminator) throws IOException
253    {
254        int retVal = 0;
255        boolean digitFound = false;
256        boolean terminatorFound = false;
257        int readInt = reader.read();
258        char charValue;
259        int intValue;
260        while ((-1 < readInt) && !terminatorFound)
261        {
262            charValue = (char)readInt;
263            intValue = Character.digit(charValue, 10);
264            if (-1 < intValue)
265            {
266                retVal = retVal * 10 + intValue;
267                digitFound = true;
268            }
269            else if (charValue == terminator)
270            {
271                terminatorFound = true;
272                if (!digitFound)
273                    retVal = -1;
274            }
275            else
276            {
277                throw new RuntimeException("Invalid terminator, expected " + terminator + ", got " + charValue);
278            }
279            if (!terminatorFound)
280            {
281                readInt = reader.read();
282            }
283        }
284        return retVal;
285    }
286
287    /**
288     * Reads a (non-negative) integer value from a stream.
289     * @param reader the reader from which the value is read.
290     * @param terminator The character after the digits of the integer value
291     * @return The integer value
292     * @throws IOException if reading from the stream fails
293     * @throws RuntimeException if the integer value is malformed (i.e. the
294     *                          terminator is missing)
295     */
296    protected static int readint(Reader reader, char terminator) throws IOException
297    {
298        int retVal = readintInternal(reader, terminator);
299        if (0 > retVal)
300        {
301            throw new RuntimeException("Invalid integer read!");
302        }
303        return retVal;
304    }
305
306    /**
307     * Reads a string value (or <code>null</code>) from a stream.
308     * <code>readstr</code> is the inverse operation of {@link writestr}.
309     * @param reader the reader from which the string is read.
310     * @param terminator The terminator character that was used in {@link writestr}.
311     * @return The string or <code>null</code>
312     * @throws IOException if reading from the stream fails
313     * @throws RuntimeException if the integer value is malformed
314     */
315    protected static String readstr(Reader reader, char terminator) throws IOException
316    {
317        String retVal = null;
318        int stringLength = readintInternal(reader, terminator);
319        if (-1 < stringLength)
320        {
321            char[] retValChars = new char[stringLength];
322            int readCharSize = reader.read(retValChars);
323            if (readCharSize != stringLength)
324            {
325                throw new RuntimeException("Short read!");
326            }
327            retVal = new String(retValChars);
328        }
329        return retVal;
330    }
331
332    /**
333     * Reads a register specification from a stream. A register specification is
334     * either a digit or the character '-'.
335     * @param reader the reader from which the specification is read.
336     * @return The register number or -1.
337     * @throws IOException if reading from the stream fails
338     * @throws RuntimeException if the register specification is malformed
339     */
340    protected static int readspec(Reader reader) throws IOException
341    {
342        int retVal;
343        int readInt = reader.read();
344        char charValue;
345        if (-1 < readInt)
346        {
347            charValue = (char)readInt;
348            if ('-' == charValue)
349            {
350                retVal = -1;
351            }
352            else
353            {
354                retVal = Character.digit(charValue, 10);
355                if (0 > retVal)
356                {
357                    throw new RuntimeException("Invalid register spec " + charValue);
358                }
359            }
360        }
361        else
362        {
363            throw new RuntimeException("Short read!");
364        }
365        return retVal;
366    }
367
368    /**
369     * Reads a linefeed from a stream.
370     * @param reader the reader from which the linefeed is read.
371     * @throws IOException if reading from the stream fails
372     * @throws RuntimeException if the character read from the stream is not a
373     *                          linefeed
374     */
375    protected static void readcr(Reader reader) throws IOException
376    {
377        int readInt = reader.read();
378        if (-1 < readInt)
379        {
380            char charValue = (char)readInt;
381            if ('\n' != charValue)
382            {
383                throw new RuntimeException("Invalid linefeed " + charValue);
384            }
385        }
386        else
387        {
388            throw new RuntimeException("Short read!");
389        }
390    }
391
392    /**
393     * loads the source of a template from a reader, whichout checking the version
394     * number of the binary file. This is helpful when updating an old stored source.
395     * @param reader the reader from which the source is read.
396     * @return The source as a string.
397     * @throws IOException if reading from the stream fails
398     */
399    public static String loadsource(Reader reader) throws IOException
400    {
401        BufferedReader bufferedReader = new BufferedReader(reader);
402        bufferedReader.readLine(); // skip header (without checking)
403        bufferedReader.readLine(); // skip version number (with checking)
404        readstr(bufferedReader, '<'); // skip start delimiter
405        readcr(bufferedReader);
406        readstr(bufferedReader, '>'); // skip end delimiter
407        readcr(bufferedReader);
408        return readstr(bufferedReader, '"');
409    }
410
411    /**
412     * loads the source of a template from a string containing the compiled
413     * template.
414     * @param bytecode of the compiled template.
415     * @return The source as a string.
416     */
417    public static String loadsource(String bytecode)
418    {
419        try
420        {
421            return loadsource(new StringReader(bytecode));
422        }
423        catch (IOException ex) // can not happen when reading from a StringReader
424        {
425            return null;
426        }
427    }
428
429    /**
430     * loads a template from a reader.
431     * @param reader the reader from which the template is read.
432     * @return The template object.
433     * @throws IOException if reading from the stream fails
434     */
435    public static InterpretedTemplate load(Reader reader) throws IOException
436    {
437        InterpretedTemplate retVal = new InterpretedTemplate();
438        BufferedReader bufferedReader = new BufferedReader(reader);
439        String header = bufferedReader.readLine();
440        if (!HEADER.equals(header))
441        {
442            throw new RuntimeException("Invalid header, expected " + HEADER + ", got " + header);
443        }
444        String version = bufferedReader.readLine();
445        if (!VERSION.equals(version))
446        {
447            throw new RuntimeException("Invalid version, expected " + VERSION + ", got " + version);
448        }
449        retVal.startdelim = readstr(bufferedReader, '<');
450        readcr(bufferedReader);
451        retVal.enddelim = readstr(bufferedReader, '>');
452        readcr(bufferedReader);
453        retVal.source = readstr(bufferedReader, '"');
454        readcr(bufferedReader);
455        int count = readint(bufferedReader, '#');
456        readcr(bufferedReader);
457        Location location = null;
458        for (int i = 0; i < count; i++)
459        {
460            int r1 = readspec(bufferedReader);
461            int r2 = readspec(bufferedReader);
462            int r3 = readspec(bufferedReader);
463            int r4 = readspec(bufferedReader);
464            int r5 = readspec(bufferedReader);
465            String code = readstr(bufferedReader, ':');
466            String arg = readstr(bufferedReader, '.');
467            int readInt = bufferedReader.read();
468            if (-1 < readInt)
469            {
470                char charValue = (char)readInt;
471                if ('^' == charValue)
472                {
473                    if (null == location)
474                    {
475                        throw new RuntimeException("No previous location!");
476                    }
477                }
478                else if ('*' == charValue)
479                {
480                    location = new Location(retVal.source, readstr(bufferedReader, '='),
481                        readint(bufferedReader, '('), readint(bufferedReader, ')'),
482                        readint(bufferedReader, '{'), readint(bufferedReader, '}'));
483                }
484                else
485                {
486                    throw new RuntimeException("Invalid location spec " + charValue);
487                }
488            }
489            else
490            {
491                throw new RuntimeException("Short read!");
492            }
493            retVal.opcodes.add(new Opcode(code, r1, r2, r3, r4, r5, arg, location));
494            readcr(bufferedReader);
495        }
496        return retVal;
497    }
498
499    /**
500     * loads a template from a string.
501     * @param bytecode of the compiled template.
502     * @return The template object.
503     * @throws IOException if reading from the stream fails
504     */
505    public static InterpretedTemplate load(String bytecode)
506    {
507        try
508        {
509            return load(new StringReader(bytecode));
510        }
511        catch (IOException ex) // can not happen, when reading from a StringReader
512        {
513            return null;
514        }
515    }
516
517    /**
518     * writes an int to a stream in such a way that the int can be reliable read
519     * again. This is done by writing the digits of the integer followed by a
520     * terminator character (that may not be a digit).
521     * @param writer the stream to which to write.
522     * @param value the int value to be written.
523     * @param terminator a terminating character written after the value.
524     * @throws IOException if writing to the stream fails
525     */
526    protected static void writeint(Writer writer, int value, char terminator) throws IOException
527    {
528        writer.write(String.valueOf(value));
529        writer.write(terminator);
530    }
531
532    /**
533     * writes a string to a stream in such a way that the string can be reliable read again.
534     * @param writer the stream to which to write.
535     * @param value the string value to be written (may be null).
536     * @param terminator a terminating character written after the string length.
537     * @throws IOException if writing to the stream fails
538     */
539    protected static void writestr(Writer writer, String value, char terminator) throws IOException
540    {
541        if (value == null)
542        {
543            writer.write(terminator);
544        }
545        else
546        {
547            writer.write(String.valueOf(value.length()));
548            writer.write(terminator);
549            writer.write(value);
550        }
551    }
552
553    /**
554     * writes a register specification to a stream (which is either a digit or '-' in case the register spec is empty.
555     * @param writer the stream to which to write.
556     * @param spec the register number or -1 in case the register spec is empty.
557     * @throws IOException if writing to the stream fails
558     */
559    protected static void writespec(Writer writer, int spec) throws IOException
560    {
561        if (spec == -1)
562            writer.write("-");
563        else
564            writer.write(String.valueOf(spec));
565    }
566
567    /**
568     * writes the Template object to a stream.
569     * @param writer the stream to which to write.
570     * @throws IOException if writing to the stream fails
571     */
572    public void dump(Writer writer) throws IOException
573    {
574        writer.write(HEADER);
575        writer.write("\n");
576        writer.write(VERSION);
577        writer.write("\n");
578        writestr(writer, startdelim, '<');
579        writer.write("\n");
580        writestr(writer, enddelim, '>');
581        writer.write("\n");
582        writestr(writer, source, '"');
583        writer.write("\n");
584        writeint(writer, opcodes.size(), '#');
585        writer.write("\n");
586        Location lastLocation = null;
587        for (int i = 0; i < opcodes.size(); ++i)
588        {
589            Opcode opcode = (Opcode)opcodes.get(i);
590            writespec(writer, opcode.r1);
591            writespec(writer, opcode.r2);
592            writespec(writer, opcode.r3);
593            writespec(writer, opcode.r4);
594            writespec(writer, opcode.r5);
595            writestr(writer, Opcode.code2name(opcode.name), ':');
596            writestr(writer, opcode.arg, '.');
597            if (opcode.location != lastLocation)
598            {
599                writer.write("*");
600                writestr(writer, opcode.location.type, '=');
601                writeint(writer, opcode.location.starttag, '(');
602                writeint(writer, opcode.location.endtag, ')');
603                writeint(writer, opcode.location.startcode, '{');
604                writeint(writer, opcode.location.endcode, '}');
605                lastLocation = opcode.location;
606            }
607            else
608            {
609                writer.write("^");
610            }
611            writer.write("\n");
612        }
613    }
614
615    /**
616     * writes the Template object to a string.
617     * @return The string containing the template in compiled format.
618     */
619    public String dumps()
620    {
621        StringWriter writer = new StringWriter();
622        try
623        {
624            dump(writer);
625        }
626        catch (IOException ex) // can not happen, when dumping to a StringWriter
627        {
628        }
629        return writer.toString();
630    }
631
632    /**
633     * Annotates all control flow opcodes in the template with a jump location.
634     * <ul>
635     * <li>(a <code>for</code> opcode gets annotated with the location of the
636     * associated <code>endfor</code> opcode;</li>
637     * <li>an <code>if</code> opcode gets annotated with the location of the
638     * associated <code>else</code> or <code>endif</code> opcode;</li>
639     * <li>an <code>else</code> opcode gets annotated with the location of
640     * <code>endif</code> opcode;</li>
641     * <li>a <code>break</code> opcode gets annotated with the location of next
642     * opcode after the associated <code>endfor</code> opcode.</li>
643     * <li>a <code>continue</code> opcode gets annotated with the location of next
644     * opcode after the associated ENDFOR opcode.</li>
645     * </ul>
646     */
647    protected void annotate()
648    {
649        if (!annotated)
650        {
651            for (int i = 0; i < opcodes.size(); ++i)
652            {
653                Opcode opcode = (Opcode)opcodes.get(i);
654                switch (opcode.name)
655                {
656                    case Opcode.OC_IF:
657                        i = annotateIf(i, 0);
658                        break;
659                    case Opcode.OC_FOR:
660                        i = annotateFor(i, 0);
661                        break;
662                    case Opcode.OC_ELSE:
663                        throw new BlockException("else outside if block");
664                    case Opcode.OC_ENDIF:
665                        throw new BlockException("endif outside if block");
666                    case Opcode.OC_ENDFOR:
667                        throw new BlockException("endfor outside for loop");
668                    case Opcode.OC_BREAK:
669                        throw new BlockException("break outside for loop");
670                    case Opcode.OC_CONTINUE:
671                        throw new BlockException("continue outside for loop");
672                }
673            }
674            annotated = true;
675        }
676    }
677
678    protected int annotateIf(int ifStart, int forDepth)
679    {
680        int jump = ifStart;
681        for (int i = ifStart+1; i < opcodes.size(); ++i)
682        {
683            Opcode opcode = (Opcode)opcodes.get(i);
684            switch (opcode.name)
685            {
686                case Opcode.OC_IF:
687                    i = annotateIf(i, forDepth);
688                    break;
689                case Opcode.OC_FOR:
690                    i = annotateFor(i, forDepth);
691                    break;
692                case Opcode.OC_ELSE:
693                    ((Opcode)opcodes.get(jump)).jump = i;
694                    jump = i;
695                    break;
696                case Opcode.OC_ENDIF:
697                    ((Opcode)opcodes.get(jump)).jump = i;
698                    return i;
699                case Opcode.OC_BREAK:
700                    if (forDepth == 0)
701                        throw new BlockException("break outside for loop");
702                    break;
703                case Opcode.OC_CONTINUE:
704                    if (forDepth == 0)
705                        throw new BlockException("continue outside for loop");
706                    break;
707                case Opcode.OC_ENDFOR:
708                    throw new BlockException("endfor in if block");
709            }
710        }
711        throw new BlockException("unclosed if block");
712    }
713
714    protected int annotateFor(int loopStart, int forDepth)
715    {
716        ++forDepth;
717        LinkedList breaks = new LinkedList();
718        LinkedList continues = new LinkedList();
719
720        for (int i = loopStart+1; i < opcodes.size(); ++i)
721        {
722            Opcode opcode = (Opcode)opcodes.get(i);
723            switch (opcode.name)
724            {
725                case Opcode.OC_IF:
726                    i = annotateIf(i, forDepth);
727                    break;
728                case Opcode.OC_FOR:
729                    i = annotateFor(i, forDepth);
730                    break;
731                case Opcode.OC_ELSE:
732                    throw new BlockException("else in for loop");
733                case Opcode.OC_ENDIF:
734                    throw new BlockException("endif in for loop");
735                case Opcode.OC_BREAK:
736                    breaks.add(new Integer(i));
737                    break;
738                case Opcode.OC_CONTINUE:
739                    continues.add(new Integer(i));
740                    break;
741                case Opcode.OC_ENDFOR:
742                    int j;
743                    int jump;
744                    for (j = 0; j < breaks.size(); ++j)
745                    {
746                        jump = ((Integer)breaks.get(j)).intValue();
747                        ((Opcode)opcodes.get(jump)).jump = i;
748                    }
749                    for (j = 0; j < continues.size(); ++j)
750                    {
751                        jump = ((Integer)continues.get(j)).intValue();
752                        ((Opcode)opcodes.get(jump)).jump = i;
753                    }
754                    ((Opcode)opcodes.get(loopStart)).jump = i;
755                    return i;
756            }
757        }
758        throw new BlockException("unclosed loop");
759    }
760
761    public Iterator render()
762    {
763        return new Renderer(null);
764    }
765
766    /**
767     * Renders the template.
768     * @param variables a map containing the top level variables that should be
769     *                  available to the template code.
770     * @return An iterator that returns the string output piece by piece.
771     */
772    public Iterator render(Map variables)
773    {
774        return new Renderer(variables);
775    }
776
777    public String renders()
778    {
779        return renders(null);
780    }
781
782    /**
783     * Renders the template and returns the resulting string.
784     * @param variables a map containing the top level variables that should be
785     *                  available to the template code.
786     * @return The render output as a string.
787     */
788    public String renders(Map variables)
789    {
790        StringBuffer output = new StringBuffer();
791
792        for (Iterator iterator = render(variables); iterator.hasNext();)
793        {
794            output.append((String)iterator.next());
795        }
796        return output.toString();
797    }
798
799    class Renderer implements Iterator
800    {
801        /**
802         * The current program counter
803         */
804        private int pc = 0;
805
806        /**
807         * The ten registers of our CPU
808         */
809        private Object[] reg = new Object[10];
810
811        /**
812         * The variables passed to the {@com.livinglogic.ul4.InterpretedTemplate#render} call
813         * During the run of the iterator loop variables and variables from
814         * <code>&lt;?code>&gt;</code> tag will be stored here
815         */
816        private Map variables;
817
818        /**
819         * The stack of active for loops
820         */
821        private LinkedList iterators = new LinkedList();
822
823        /**
824         * If a subtemplate is running (i.e. if we're inside a
825         * <code>&lt;?render?&gt;</code> tag), this variable references the
826         * active part iterator for the subtemplate.
827         */
828        private Iterator subTemplateIterator = null;
829
830        /**
831         * Since we implement the iterator interface we have to support both
832         * <code>next</code> and <code>hasNext</code>. This means that neither
833         * of the two methods can directly run the opcodes to get the next output
834         * chunk. Instead of that we have a method {@link getNextChunk} that runs
835         * the opcodes until the next output chunk is produced and stores it in
836         * <code>nextChunk</code>, when both <code>next</code> and
837         * <code>hasNext</code> can refer to it.
838         */
839        private String nextChunk = null;
840
841        public Renderer(Map variables)
842        {
843            annotate();
844            if (variables == null)
845                variables = new HashMap();
846            this.variables = variables;
847            getNextChunk();
848        }
849
850        public void remove()
851        {
852            throw new UnsupportedOperationException("remove() not supported for " + getClass() + "!");
853        }
854
855        public boolean hasNext()
856        {
857            return nextChunk != null;
858        }
859
860        public Object next()
861        {
862            String result = nextChunk;
863            getNextChunk();
864            return result;
865        }
866
867        /**
868         * Gets the next output chunk and stores it in {@link nextChunk}
869         */
870        private void getNextChunk()
871        {
872            if (subTemplateIterator != null)
873            {
874                if (subTemplateIterator.hasNext())
875                {
876                    nextChunk = (String)subTemplateIterator.next();
877                    return;
878                }
879                else
880                {
881                    subTemplateIterator = null;
882                }
883            }
884            while (pc < opcodes.size())
885            {
886                Opcode code = (Opcode)opcodes.get(pc);
887
888                try
889                {
890                    switch (code.name)
891                    {
892                        case Opcode.OC_TEXT:
893                            nextChunk = code.location.getCode();
894                            ++pc;
895                            return;
896                        case Opcode.OC_PRINT:
897                            nextChunk = ObjectUtils.toString(reg[code.r1]);
898                            ++pc;
899                            return;
900                        case Opcode.OC_PRINTX:
901                            nextChunk = Utils.xmlescape(ObjectUtils.toString(reg[code.r1]));
902                            ++pc;
903                            return;
904                        case Opcode.OC_LOADNONE:
905                            reg[code.r1] = null;
906                            break;
907                        case Opcode.OC_LOADFALSE:
908                            reg[code.r1] = Boolean.FALSE;
909                            break;
910                        case Opcode.OC_LOADTRUE:
911                            reg[code.r1] = Boolean.TRUE;
912                            break;
913                        case Opcode.OC_LOADSTR:
914                            reg[code.r1] = code.arg;
915                            break;
916                        case Opcode.OC_LOADINT:
917                            reg[code.r1] = new Integer(Integer.parseInt(code.arg));
918                            break;
919                        case Opcode.OC_LOADFLOAT:
920                            reg[code.r1] = new Double(Double.parseDouble(code.arg));
921                            break;
922                        case Opcode.OC_LOADDATE:
923                            reg[code.r1] = Utils.isoDateFormatter.parse(code.arg);
924                            break;
925                        case Opcode.OC_LOADCOLOR:
926                            reg[code.r1] = Color.fromdump(code.arg);
927                            break;
928                        case Opcode.OC_BUILDLIST:
929                            reg[code.r1] = new ArrayList();
930                            break;
931                        case Opcode.OC_BUILDDICT:
932                            reg[code.r1] = new HashMap();
933                            break;
934                        case Opcode.OC_ADDLIST:
935                            ((List)reg[code.r1]).add(reg[code.r2]);
936                            break;
937                        case Opcode.OC_ADDDICT:
938                            ((Map)reg[code.r1]).put(reg[code.r2], reg[code.r3]);
939                            break;
940                        case Opcode.OC_UPDATEDICT:
941                            ((Map)reg[code.r1]).putAll((Map)reg[code.r2]);
942                            break;
943                        case Opcode.OC_LOADVAR:
944                            reg[code.r1] = variables.get(code.arg);
945                            break;
946                        case Opcode.OC_STOREVAR:
947                            variables.put(code.arg, reg[code.r1]);
948                            break;
949                        case Opcode.OC_ADDVAR:
950                            variables.put(code.arg, Utils.add(variables.get(code.arg), reg[code.r1]));
951                            break;
952                        case Opcode.OC_SUBVAR:
953                            variables.put(code.arg, Utils.sub(variables.get(code.arg), reg[code.r1]));
954                            break;
955                        case Opcode.OC_MULVAR:
956                            variables.put(code.arg, Utils.mul(variables.get(code.arg), reg[code.r1]));
957                            break;
958                        case Opcode.OC_TRUEDIVVAR:
959                            variables.put(code.arg, Utils.truediv(variables.get(code.arg), reg[code.r1]));
960                            break;
961                        case Opcode.OC_FLOORDIVVAR:
962                            variables.put(code.arg, Utils.floordiv(variables.get(code.arg), reg[code.r1]));
963                            break;
964                        case Opcode.OC_MODVAR:
965                            variables.put(code.arg, Utils.mod(variables.get(code.arg), reg[code.r1]));
966                            break;
967                        case Opcode.OC_DELVAR:
968                            variables.remove(code.arg);
969                            break;
970                        case Opcode.OC_FOR:
971                            Iterator iterator = Utils.iterator(reg[code.r2]);
972                            if (iterator.hasNext())
973                            {
974                                reg[code.r1] = iterator.next();
975                                iterators.add(new IteratorStackEntry(code.r1, pc, code.jump, iterator));
976                            }
977                            else
978                            {
979                                pc = code.jump+1;
980                                continue;
981                            }
982                            break;
983                        case Opcode.OC_BREAK:
984                        {
985                            IteratorStackEntry entry = (IteratorStackEntry)iterators.getLast();
986                            pc = entry.pcEndFor;
987                            iterators.removeLast();
988                            break;
989                        }
990                        case Opcode.OC_CONTINUE:
991                        {
992                            IteratorStackEntry entry = (IteratorStackEntry)iterators.getLast();
993                            pc = entry.pcEndFor;
994                            // Fall through
995                        }
996                        case Opcode.OC_ENDFOR:
997                        {
998                            IteratorStackEntry entry = (IteratorStackEntry)iterators.getLast();
999                            if (entry.iterator.hasNext())
1000                            {
1001                                reg[entry.iteratorRegSpec] = entry.iterator.next();
1002                                pc = entry.pcFor;
1003                            }
1004                            else
1005                            {
1006                                iterators.removeLast();
1007                            }
1008                            break;
1009                        }
1010                        case Opcode.OC_IF:
1011                            if (!Utils.getBool(reg[code.r1]))
1012                            {
1013                                pc = code.jump+1;
1014                                continue;
1015                            }
1016                            break;
1017                        case Opcode.OC_ELSE:
1018                            pc = code.jump+1;
1019                            continue;
1020                        case Opcode.OC_ENDIF:
1021                            // Skip to next opcode
1022                            break;
1023                        case Opcode.OC_GETATTR:
1024                            reg[code.r1] = Utils.getItem(reg[code.r2], code.arg);
1025                            break;
1026                        case Opcode.OC_GETITEM:
1027                            reg[code.r1] = Utils.getItem(reg[code.r2], reg[code.r3]);
1028                            break;
1029                        case Opcode.OC_GETSLICE12:
1030                            reg[code.r1] = Utils.getSlice(reg[code.r2], reg[code.r3], reg[code.r4]);
1031                            break;
1032                        case Opcode.OC_GETSLICE1:
1033                            reg[code.r1] = Utils.getSlice(reg[code.r2], reg[code.r3], null);
1034                            break;
1035                        case Opcode.OC_GETSLICE2:
1036                            reg[code.r1] = Utils.getSlice(reg[code.r2], null, reg[code.r3]);
1037                            break;
1038                        case Opcode.OC_NOT:
1039                            reg[code.r1] = Utils.getBool(reg[code.r2]) ? Boolean.FALSE : Boolean.TRUE;
1040                            break;
1041                        case Opcode.OC_NEG:
1042                            reg[code.r1] = Utils.neg(reg[code.r2]);
1043                            break;
1044                        case Opcode.OC_EQ:
1045                            reg[code.r1] = ObjectUtils.equals(reg[code.r2], reg[code.r3]) ? Boolean.TRUE : Boolean.FALSE;
1046                            break;
1047                        case Opcode.OC_NE:
1048                            reg[code.r1] = ObjectUtils.equals(reg[code.r2], reg[code.r3]) ? Boolean.FALSE : Boolean.TRUE;
1049                            break;
1050                        case Opcode.OC_LT:
1051                            reg[code.r1] = Utils.lt(reg[code.r2], reg[code.r3]) ? Boolean.TRUE : Boolean.FALSE;
1052                            break;
1053                        case Opcode.OC_LE:
1054                            reg[code.r1] = Utils.le(reg[code.r2], reg[code.r3]) ? Boolean.TRUE : Boolean.FALSE;
1055                            break;
1056                        case Opcode.OC_GT:
1057                            reg[code.r1] = Utils.le(reg[code.r2], reg[code.r3]) ? Boolean.FALSE : Boolean.TRUE;
1058                            break;
1059                        case Opcode.OC_GE:
1060                            reg[code.r1] = Utils.lt(reg[code.r2], reg[code.r3]) ? Boolean.FALSE : Boolean.TRUE;
1061                            break;
1062                        case Opcode.OC_NOTCONTAINS:
1063                            reg[code.r1] = Utils.contains(reg[code.r2], reg[code.r3]) ? Boolean.FALSE : Boolean.TRUE;
1064                            break;
1065                        case Opcode.OC_OR:
1066                            reg[code.r1] = Utils.getBool(reg[code.r2]) ? reg[code.r2] : reg[code.r3];
1067                            break;
1068                        case Opcode.OC_AND:
1069                            reg[code.r1] = Utils.getBool(reg[code.r3]) ? reg[code.r2] : reg[code.r3];
1070                            break;
1071                        case Opcode.OC_ADD:
1072                            reg[code.r1] = Utils.add(reg[code.r2], reg[code.r3]);
1073                            break;
1074                        case Opcode.OC_SUB:
1075                            reg[code.r1] = Utils.sub(reg[code.r2], reg[code.r3]);
1076                            break;
1077                        case Opcode.OC_MUL:
1078                            reg[code.r1] = Utils.mul(reg[code.r2], reg[code.r3]);
1079                            break;
1080                        case Opcode.OC_TRUEDIV:
1081                            reg[code.r1] = Utils.truediv(reg[code.r2], reg[code.r3]);
1082                            break;
1083                        case Opcode.OC_FLOORDIV:
1084                            reg[code.r1] = Utils.floordiv(reg[code.r2], reg[code.r3]);
1085                            break;
1086                        case Opcode.OC_MOD:
1087                            reg[code.r1] = Utils.mod(reg[code.r2], reg[code.r3]);
1088                            break;
1089                        case Opcode.OC_CALLFUNC0:
1090                            switch (code.argcode)
1091                            {
1092                                case Opcode.CF0_NOW:
1093                                    reg[code.r1] = new Date();
1094                                    break;
1095                                case Opcode.CF0_VARS:
1096                                    reg[code.r1] = variables;
1097                                    break;
1098                            }
1099                            break;
1100                        case Opcode.OC_CALLFUNC1:
1101                            switch (code.argcode)
1102                            {
1103                                case Opcode.CF1_XMLESCAPE:
1104                                    reg[code.r1] = Utils.xmlescape(reg[code.r2]);
1105                                    break;
1106                                case Opcode.CF1_CSV:
1107                                    reg[code.r1] = Utils.csv(reg[code.r2]);
1108                                    break;
1109                                case Opcode.CF1_STR:
1110                                    reg[code.r1] = ObjectUtils.toString(reg[code.r2]);
1111                                    break;
1112                                case Opcode.CF1_REPR:
1113                                    reg[code.r1] = Utils.repr(reg[code.r2]);
1114                                    break;
1115                                case Opcode.CF1_INT:
1116                                    reg[code.r1] = Utils.toInteger(reg[code.r2]);
1117                                    break;
1118                                case Opcode.CF1_BOOL:
1119                                    reg[code.r1] = Utils.getBool(reg[code.r2]) ? Boolean.TRUE : Boolean.FALSE;
1120                                    break;
1121                                case Opcode.CF1_LEN:
1122                                    reg[code.r1] = Utils.length(reg[code.r2]);
1123                                    break;
1124                                case Opcode.CF1_ENUMERATE:
1125                                    reg[code.r1] = Utils.enumerate(reg[code.r2]);
1126                                    break;
1127                                case Opcode.CF1_ISNONE:
1128                                    reg[code.r1] = (null == reg[code.r2]) ? Boolean.TRUE : Boolean.FALSE;
1129                                    break;
1130                                case Opcode.CF1_ISSTR:
1131                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof String)) ? Boolean.TRUE : Boolean.FALSE;
1132                                    break;
1133                                case Opcode.CF1_ISINT:
1134                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Integer)) ? Boolean.TRUE : Boolean.FALSE;
1135                                    break;
1136                                case Opcode.CF1_ISFLOAT:
1137                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Double)) ? Boolean.TRUE : Boolean.FALSE;
1138                                    break;
1139                                case Opcode.CF1_ISBOOL:
1140                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Boolean)) ? Boolean.TRUE : Boolean.FALSE;
1141                                    break;
1142                                case Opcode.CF1_ISDATE:
1143                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Date)) ? Boolean.TRUE : Boolean.FALSE;
1144                                    break;
1145                                case Opcode.CF1_ISLIST:
1146                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof List)) ? Boolean.TRUE : Boolean.FALSE;
1147                                    break;
1148                                case Opcode.CF1_ISDICT:
1149                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Map)) ? Boolean.TRUE : Boolean.FALSE;
1150                                    break;
1151                                case Opcode.CF1_ISTEMPLATE:
1152                                    reg[code.r1] = ((null != reg[code.r2]) && (reg[code.r2] instanceof Template)) ? Boolean.TRUE : Boolean.FALSE;
1153                                    break;
1154                                case Opcode.CF1_CHR:
1155                                    reg[code.r1] = Utils.chr(reg[code.r2]);
1156                                    break;
1157                                case Opcode.CF1_ORD:
1158                                    reg[code.r1] = Utils.ord(reg[code.r2]);
1159                                    break;
1160                                case Opcode.CF1_HEX:
1161                                    reg[code.r1] = Utils.hex(reg[code.r2]);
1162                                    break;
1163                                case Opcode.CF1_OCT:
1164                                    reg[code.r1] = Utils.oct(reg[code.r2]);
1165                                    break;
1166                                case Opcode.CF1_BIN:
1167                                    reg[code.r1] = Utils.bin(reg[code.r2]);
1168                                    break;
1169                                case Opcode.CF1_SORTED:
1170                                    reg[code.r1] = Utils.sorted(reg[code.r2]);
1171                                    break;
1172                                case Opcode.CF1_RANGE:
1173                                    reg[code.r1] = Utils.range(reg[code.r2]);
1174                                    break;
1175                                case Opcode.CF1_TYPE:
1176                                    reg[code.r1] = Utils.type(reg[code.r2]);
1177                                    break;
1178                                case Opcode.CF1_GET:
1179                                    reg[code.r1] = variables.get(reg[code.r2]);
1180                                    break;
1181                                case Opcode.CF1_JSON:
1182                                    reg[code.r1] = Utils.json(reg[code.r2]);
1183                                    break;
1184                            }
1185                            break;
1186                        case Opcode.OC_CALLFUNC2:
1187                            switch (code.argcode)
1188                            {
1189                                case Opcode.CF2_RANGE:
1190                                    reg[code.r1] = Utils.range(reg[code.r2], reg[code.r3]);
1191                                    break;
1192                                case Opcode.CF2_GET:
1193                                    reg[code.r1] = variables.containsKey(reg[code.r2]) ? variables.get(reg[code.r2]) : reg[code.r3];
1194                                    break;
1195                                case Opcode.CF2_ZIP:
1196                                    reg[code.r1] = Utils.zip(reg[code.r2], reg[code.r3]);
1197                                    break;
1198                                case Opcode.CF2_INT:
1199                                    reg[code.r1] = Utils.toInteger(reg[code.r2], reg[code.r3]);
1200                                    break;
1201                            }
1202                            break;
1203                        case Opcode.OC_CALLFUNC3:
1204                            switch (code.argcode)
1205                            {
1206                                case Opcode.CF3_RANGE:
1207                                    reg[code.r1] = Utils.range(reg[code.r2], reg[code.r3], reg[code.r4]);
1208                                    break;
1209                                case Opcode.CF3_ZIP:
1210                                    reg[code.r1] = Utils.zip(reg[code.r2], reg[code.r3], reg[code.r4]);
1211                                    break;
1212                                case Opcode.CF3_RGB:
1213                                    reg[code.r1] = Utils.rgb(reg[code.r2], reg[code.r3], reg[code.r4]);
1214                                    break;
1215                                case Opcode.CF3_HLS:
1216                                    reg[code.r1] = Utils.hls(reg[code.r2], reg[code.r3], reg[code.r4]);
1217                                    break;
1218                                case Opcode.CF3_HSV:
1219                                    reg[code.r1] = Utils.hsv(reg[code.r2], reg[code.r3], reg[code.r4]);
1220                                    break;
1221                            }
1222                            break;
1223                        case Opcode.OC_CALLFUNC4:
1224                            switch (code.argcode)
1225                            {
1226                                case Opcode.CF4_RGB:
1227                                    reg[code.r1] = Utils.rgb(reg[code.r2], reg[code.r3], reg[code.r4], reg[code.r5]);
1228                                    break;
1229                                case Opcode.CF4_HLS:
1230                                    reg[code.r1] = Utils.hls(reg[code.r2], reg[code.r3], reg[code.r4], reg[code.r5]);
1231                                    break;
1232                                case Opcode.CF4_HSV:
1233                                    reg[code.r1] = Utils.hsv(reg[code.r2], reg[code.r3], reg[code.r4], reg[code.r5]);
1234                                    break;
1235                            }
1236                            break;
1237                        case Opcode.OC_CALLMETH0:
1238                            switch (code.argcode)
1239                            {
1240                                case Opcode.CM0_SPLIT:
1241                                    reg[code.r1] = Utils.split(reg[code.r2]);
1242                                    break;
1243                                case Opcode.CM0_STRIP:
1244                                    reg[code.r1] = Utils.strip(reg[code.r2]);
1245                                    break;
1246                                case Opcode.CM0_LSTRIP:
1247                                    reg[code.r1] = Utils.lstrip(reg[code.r2]);
1248                                    break;
1249                                case Opcode.CM0_RSTRIP:
1250                                    reg[code.r1] = Utils.rstrip(reg[code.r2]);
1251                                    break;
1252                                case Opcode.CM0_UPPER:
1253                                    reg[code.r1] = Utils.upper(reg[code.r2]);
1254                                    break;
1255                                case Opcode.CM0_LOWER:
1256                                    reg[code.r1] = Utils.lower(reg[code.r2]);
1257                                    break;
1258                                case Opcode.CM0_CAPITALIZE:
1259                                    reg[code.r1] = Utils.capitalize(reg[code.r2]);
1260                                    break;
1261                                case Opcode.CM0_ITEMS:
1262                                    reg[code.r1] = Utils.items(reg[code.r2]);
1263                                    break;
1264                                case Opcode.CM0_ISOFORMAT:
1265                                    reg[code.r1] = Utils.isoformat(reg[code.r2]);
1266                                    break;
1267                                case Opcode.CM0_HLS:
1268                                    reg[code.r1] = ((Color)reg[code.r2]).hls();
1269                                    break;
1270                                case Opcode.CM0_HLSA:
1271                                    reg[code.r1] = ((Color)reg[code.r2]).hlsa();
1272                                    break;
1273                                case Opcode.CM0_HSV:
1274                                    reg[code.r1] = ((Color)reg[code.r2]).hsv();
1275                                    break;
1276                                case Opcode.CM0_HSVA:
1277                                    reg[code.r1] = ((Color)reg[code.r2]).hsva();
1278                                    break;
1279                                case Opcode.CM0_LUM:
1280                                    reg[code.r1] = new Double(((Color)reg[code.r2]).lum());
1281                                    break;
1282                            }
1283                            break;
1284                        case Opcode.OC_CALLMETH1:
1285                            switch (code.argcode)
1286                            {
1287                                case Opcode.CM1_SPLIT:
1288                                    reg[code.r1] = Utils.split(reg[code.r2], reg[code.r3]);
1289                                    break;
1290/*                              case Opcode.CM1_RSPLIT:
1291                                    reg[code.r1] = Utils.rsplit(reg[code.r2], reg[code.r3]);
1292                                    break;
1293                                case Opcode.CM1_STRIP:
1294                                    reg[code.r1] = Utils.strip(reg[code.r2], reg[code.r3]);
1295                                    break;
1296                                case Opcode.CM1_LSTRIP:
1297                                    reg[code.r1] = Utils.lstrip(reg[code.r2], reg[code.r3]);
1298                                    break;
1299                                case Opcode.CM1_RSTRIP:
1300                                    reg[code.r1] = Utils.rstrip(reg[code.r2], reg[code.r3]);
1301                                    break;
1302                                case Opcode.CM1_STARTSWITH:
1303                                    reg[code.r1] = Utils.startswith(reg[code.r2], reg[code.r3]);
1304                                    break;
1305                                case Opcode.CM1_ENDSWITH:
1306                                    reg[code.r1] = Utils.endswith(reg[code.r2], reg[code.r3]);
1307                                    break;*/
1308                                case Opcode.CM1_FIND:
1309                                    reg[code.r1] = Utils.find(reg[code.r2], reg[code.r3]);
1310                                    break;
1311                                case Opcode.CM1_RFIND:
1312                                    reg[code.r1] = Utils.rfind(reg[code.r2], reg[code.r3]);
1313                                    break;
1314                                case Opcode.CM1_FORMAT:
1315                                    reg[code.r1] = Utils.format(reg[code.r2], reg[code.r3], defaultLocale);
1316                                    break;
1317                                case Opcode.CM1_GET:
1318                                    reg[code.r1] = ((Map)reg[code.r2]).get(reg[code.r3]);
1319                                    break;
1320                                case Opcode.CM1_WITHLUM:
1321                                    reg[code.r1] = Utils.withlum(reg[code.r2], reg[code.r3]);
1322                                    break;
1323                                case Opcode.CM1_WITHA:
1324                                    reg[code.r1] = Utils.witha(reg[code.r2], reg[code.r3]);
1325                                    break;
1326                            }
1327                            break;
1328                        case Opcode.OC_CALLMETH2:
1329                            switch (code.argcode)
1330                            {
1331                                case Opcode.CM2_REPLACE:
1332                                    reg[code.r1] = Utils.replace(reg[code.r2], reg[code.r3], reg[code.r4]);
1333                                    break;
1334                                case Opcode.CM2_GET:
1335                                    reg[code.r1] = ((Map)reg[code.r2]).containsKey(reg[code.r3]) ? ((Map)reg[code.r2]).get(reg[code.r3]) : reg[code.r4];
1336                                    break;
1337                            }
1338                            break;
1339                        case Opcode.OC_CALLMETH3:
1340                            throw new UnknownMethodException(code.arg);
1341                        case Opcode.OC_CALLMETHKW:
1342                            switch (code.argcode)
1343                            {
1344                                case Opcode.CMKW_RENDER:
1345                                    reg[code.r1] = ((Template)reg[code.r2]).renders((Map)reg[code.r3]);
1346                                    break;
1347                                default:
1348                                    throw new UnknownMethodException(code.arg);
1349                            }
1350                            break;
1351                        case Opcode.OC_RENDER:
1352                            if (reg[code.r1] instanceof InterpretedTemplate)
1353                            {
1354                                subTemplateIterator = ((InterpretedTemplate)reg[code.r1]).render((Map)reg[code.r2]);
1355                                if (subTemplateIterator.hasNext())
1356                                {
1357                                    nextChunk = (String)subTemplateIterator.next();
1358                                    ++pc;
1359                                    return;
1360                                }
1361                                else
1362                                {
1363                                    subTemplateIterator = null;
1364                                }
1365                                break;
1366                            }
1367                            else
1368                                nextChunk = ((Template)reg[code.r1]).renders((Map)reg[code.r2]);
1369                        default:
1370                            throw new RuntimeException("Unknown opcode '" + code.name + "'!");
1371                    }
1372                }
1373                catch (Exception ex)
1374                {
1375                    throw new LocationException(ex, code.location);
1376                }
1377                ++pc;
1378            }
1379            // finished => no next chunk available
1380            nextChunk = null;
1381        }
1382    }
1383
1384    public static List tokenizeTags(String source, String startdelim, String enddelim)
1385    {
1386        Pattern tagPattern = Pattern.compile(escapeREchars(startdelim) + "(printx|print|code|for|if|elif|else|end|break|continue|render|note)(\\s*((.|\\n)*?)\\s*)?" + escapeREchars(enddelim));
1387        LinkedList tags = new LinkedList();
1388        Matcher matcher = tagPattern.matcher(source);
1389        int pos = 0;
1390
1391        int start;
1392        int end;
1393        while (matcher.find())
1394        {
1395            start = matcher.start();
1396            end = start + matcher.group().length();
1397            if (pos != start)
1398                tags.add(new Location(source, null, pos, start, pos, start));
1399            int codestart = matcher.start(3);
1400            int codeend = codestart + matcher.group(3).length();
1401            String type = matcher.group(1);
1402            if (!type.equals("note"))
1403                tags.add(new Location(source, matcher.group(1), start, end, codestart, codeend));
1404            pos = end;
1405        }
1406        end = source.length();
1407        if (pos != end)
1408            tags.add(new Location(source, null, pos, end, pos, end));
1409        return tags;
1410    }
1411
1412    private static String escapeREchars(String input)
1413    {
1414        int len = input.length();
1415
1416        StringBuffer output = new StringBuffer(len);
1417
1418        for (int i = 0; i < len; ++i)
1419        {
1420            char c = input.charAt(i);
1421            if (!(('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || ('0' <= c && c <= '9')))
1422                output.append('\\');
1423            output.append(c);
1424        }
1425        return output.toString();
1426    }
1427
1428    public static List tokenizeCode(Location location) throws LexicalException
1429    {
1430        String source = location.getCode();
1431
1432        LinkedList tokens = new LinkedList();
1433
1434        int pos = 0;
1435        int stringStartPos = 0; // The starting position of a string constant
1436        int stringMode = 0; // 0 == default; 1 == single-quoted string; 2 == double-quoted strings
1437        StringBuffer collectString = null; // characters are collected here, while we're in a string constant
1438
1439        try
1440        {
1441            while (source.length() != 0)
1442            {
1443                Matcher tokenMatcher = tokenPattern.matcher(source);
1444                Matcher nameMatcher = namePattern.matcher(source);
1445                Matcher floatMatcher = floatPattern.matcher(source);
1446                Matcher hexintMatcher = hexintPattern.matcher(source);
1447                Matcher octintMatcher = octintPattern.matcher(source);
1448                Matcher binintMatcher = binintPattern.matcher(source);
1449                Matcher intMatcher = intPattern.matcher(source);
1450                Matcher dateMatcher = datePattern.matcher(source);
1451                Matcher color3Matcher = color3Pattern.matcher(source);
1452                Matcher color4Matcher = color4Pattern.matcher(source);
1453                Matcher color6Matcher = color6Pattern.matcher(source);
1454                Matcher color8Matcher = color8Pattern.matcher(source);
1455                Matcher whitespaceMatcher = whitespacePattern.matcher(source);
1456                Matcher escaped8BitCharMatcher = escaped8BitCharPattern.matcher(source);
1457                Matcher escaped16BitCharMatcher = escaped16BitCharPattern.matcher(source);
1458                Matcher escaped32BitCharMatcher = escaped32BitCharPattern.matcher(source);
1459
1460                int len;
1461                if (stringMode==0 && tokenMatcher.lookingAt())
1462                {
1463                    len = tokenMatcher.end();
1464                    tokens.add(new Token(pos, pos+len, tokenMatcher.group()));
1465                }
1466                else if (stringMode==0 && nameMatcher.lookingAt())
1467                {
1468                    len = nameMatcher.end();
1469                    String name = nameMatcher.group();
1470                    if (name.equals("in") || name.equals("not") || name.equals("or") || name.equals("and") || name.equals("del"))
1471                        tokens.add(new Token(pos, pos+len, name));
1472                    else if (name.equals("None"))
1473                        tokens.add(new LoadNone(pos, pos+len));
1474                    else if (name.equals("True"))
1475                        tokens.add(new LoadTrue(pos, pos+len));
1476                    else if (name.equals("False"))
1477                        tokens.add(new LoadFalse(pos, pos+len));
1478                    else
1479                        tokens.add(new Name(pos, pos+len, name));
1480                }
1481                else if (stringMode==0 && dateMatcher.lookingAt())
1482                {
1483                    len = dateMatcher.end();
1484                    tokens.add(new LoadDate(pos, pos+len, Utils.isoDateFormatter.parse(dateMatcher.group())));
1485                }
1486                else if (stringMode==0 && color8Matcher.lookingAt())
1487                {
1488                    len = color8Matcher.end();
1489                    String value = color8Matcher.group();
1490                    int r = Integer.valueOf(value.substring(1, 3), 16).intValue();
1491                    int g = Integer.valueOf(value.substring(3, 5), 16).intValue();
1492                    int b = Integer.valueOf(value.substring(5, 7), 16).intValue();
1493                    int a = Integer.valueOf(value.substring(7, 9), 16).intValue();
1494                    tokens.add(new LoadColor(pos, pos+len, new Color(r, g, b, a)));
1495                }
1496                else if (stringMode==0 && color6Matcher.lookingAt())
1497                {
1498                    len = color6Matcher.end();
1499                    String value = color6Matcher.group();
1500                    int r = Integer.valueOf(value.substring(1, 3), 16).intValue();
1501                    int g = Integer.valueOf(value.substring(3, 5), 16).intValue();
1502                    int b = Integer.valueOf(value.substring(5, 7), 16).intValue();
1503                    tokens.add(new LoadColor(pos, pos+len, new Color(r, g, b)));
1504                }
1505                else if (stringMode==0 && color4Matcher.lookingAt())
1506                {
1507                    len = color4Matcher.end();
1508                    String value = color4Matcher.group();
1509                    int r = 17*Integer.valueOf(value.substring(1, 2), 16).intValue();
1510                    int g = 17*Integer.valueOf(value.substring(2, 3), 16).intValue();
1511                    int b = 17*Integer.valueOf(value.substring(3, 4), 16).intValue();
1512                    int a = 17*Integer.valueOf(value.substring(4, 5), 16).intValue();
1513                    tokens.add(new LoadColor(pos, pos+len, new Color(r, g, b, a)));
1514                }
1515                else if (stringMode==0 && color3Matcher.lookingAt())
1516                {
1517                    len = color3Matcher.end();
1518                    String value = color3Matcher.group();
1519                    int r = 17*Integer.valueOf(value.substring(1, 2), 16).intValue();
1520                    int g = 17*Integer.valueOf(value.substring(2, 3), 16).intValue();
1521                    int b = 17*Integer.valueOf(value.substring(3, 4), 16).intValue();
1522                    tokens.add(new LoadColor(pos, pos+len, new Color(r, g, b)));
1523                }
1524                else if (stringMode==0 && floatMatcher.lookingAt())
1525                {
1526                    len = floatMatcher.end();
1527                    tokens.add(new LoadFloat(pos, pos+len, Double.parseDouble(floatMatcher.group())));
1528                }
1529                else if (stringMode==0 && hexintMatcher.lookingAt())
1530                {
1531                    len = hexintMatcher.end();
1532                    tokens.add(new LoadInt(pos, pos+len, Integer.parseInt(hexintMatcher.group().substring(2), 16)));
1533                }
1534                else if (stringMode==0 && octintMatcher.lookingAt())
1535                {
1536                    len = octintMatcher.end();
1537                    tokens.add(new LoadInt(pos, pos+len, Integer.parseInt(octintMatcher.group().substring(2), 8)));
1538                }
1539                else if (stringMode==0 && binintMatcher.lookingAt())
1540                {
1541                    len = binintMatcher.end();
1542                    tokens.add(new LoadInt(pos, pos+len, Integer.parseInt(binintMatcher.group().substring(2), 2)));
1543                }
1544                else if (stringMode==0 && intMatcher.lookingAt())
1545                {
1546                    len = intMatcher.end();
1547                    tokens.add(new LoadInt(pos, pos+len, Integer.parseInt(intMatcher.group())));
1548                }
1549                else if (stringMode==0 && source.startsWith("'"))
1550                {
1551                    stringStartPos = pos;
1552                    len = 1;
1553                    stringMode = 1;
1554                    collectString = new StringBuffer();
1555                }
1556                else if (stringMode==0 && source.startsWith("\""))
1557                {
1558                    stringStartPos = pos;
1559                    len = 1;
1560                    stringMode = 2;
1561                    collectString = new StringBuffer();
1562                }
1563                else if (stringMode==1 && source.startsWith("'") || (stringMode==2 && source.startsWith("\"")))
1564                {
1565                    len = 1;
1566                    stringMode = 0;
1567                    tokens.add(new LoadStr(stringStartPos, pos+len, collectString.toString()));
1568                    collectString = null;
1569                }
1570                else if (stringMode==0 && whitespaceMatcher.lookingAt())
1571                {
1572                    len = whitespaceMatcher.end();
1573                }
1574                else if (stringMode!=0 && source.startsWith("\\\\"))
1575                {
1576                    len = 2;
1577                    collectString.append("\\");
1578                }
1579                else if (stringMode!=0 && source.startsWith("\\'"))
1580                {
1581                    len = 2;
1582                    collectString.append("'");
1583                }
1584                else if (stringMode!=0 && source.startsWith("\\\""))
1585                {
1586                    len = 2;
1587                    collectString.append("\"");
1588                }
1589                else if (stringMode!=0 && source.startsWith("\\a"))
1590                {
1591                    len = 2;
1592                    collectString.append("\u0007");
1593                }
1594                else if (stringMode!=0 && source.startsWith("\\b"))
1595                {
1596                    len = 2;
1597                    collectString.append("\u0008");
1598                }
1599                else if (stringMode!=0 && source.startsWith("\\f"))
1600                {
1601                    len = 2;
1602                    collectString.append("\u000c");
1603                }
1604                else if (stringMode!=0 && source.startsWith("\\n"))
1605                {
1606                    len = 2;
1607                    collectString.append("\n");
1608                }
1609                else if (stringMode!=0 && source.startsWith("\\r"))
1610                {
1611                    len = 2;
1612                    collectString.append("\r");
1613                }
1614                else if (stringMode!=0 && source.startsWith("\\t"))
1615                {
1616                    len = 2;
1617                    collectString.append("\t");
1618                }
1619                else if (stringMode!=0 && source.startsWith("\\v"))
1620                {
1621                    len = 2;
1622                    collectString.append("\u000b");
1623                }
1624                else if (stringMode!=0 && source.startsWith("\\e"))
1625                {
1626                    len = 2;
1627                    collectString.append("\u001b");
1628                }
1629                else if (stringMode!=0 && escaped8BitCharMatcher.lookingAt())
1630                {
1631                    len = 4;
1632                    collectString.append((char)Integer.parseInt(escaped8BitCharMatcher.group().substring(2), 16));
1633                }
1634                else if (stringMode!=0 && escaped16BitCharMatcher.lookingAt())
1635                {
1636                    len = 6;
1637                    collectString.append((char)Integer.parseInt(escaped16BitCharMatcher.group().substring(2), 16));
1638                }
1639                else if (stringMode!=0 && escaped32BitCharMatcher.lookingAt())
1640                {
1641                    len = 10;
1642                    throw new RuntimeException("character " + escaped32BitCharMatcher.group() + " (outside the BMP) not supported");
1643                }
1644                else if (stringMode!=0)
1645                {
1646                    len = 1;
1647                    collectString.append(source.charAt(0));
1648                }
1649                else
1650                {
1651                    throw new LexicalException(pos, pos+1, source.substring(0, 1));
1652                }
1653                pos += len;
1654                source = source.substring(len);
1655            }
1656            if (stringMode != 0)
1657                throw new UnterminatedStringException();
1658        }
1659        catch (LocationException ex)
1660        {
1661            throw ex;
1662        }
1663        catch (Exception ex)
1664        {
1665            // decorate inner exception with location information
1666            throw new LocationException(ex, location);
1667        }
1668        return tokens;
1669    }
1670
1671    public String toString()
1672    {
1673        StringBuffer buffer = new StringBuffer();
1674        int indent = 0;
1675
1676        int size = opcodes.size();
1677        for (int i = 0; i < size; ++i)
1678        {
1679            Opcode code = (Opcode)opcodes.get(i);
1680
1681            if (code.name == Opcode.OC_ELSE || code.name == Opcode.OC_ENDIF || code.name == Opcode.OC_ENDFOR)
1682                --indent;
1683            for (int j = 0; j < indent; ++j)
1684                buffer.append("\t");
1685            if (code.name == Opcode.OC_ENDIF || code.name == Opcode.OC_ENDFOR)
1686                buffer.append("}");
1687            else if (code.name == Opcode.OC_FOR || code.name == Opcode.OC_IF)
1688                buffer.append(code + " {");
1689            else if (code.name == Opcode.OC_ELSE)
1690                buffer.append("} else {");
1691            else
1692                buffer.append(code);
1693            buffer.append("\n");
1694            if (code.name == Opcode.OC_FOR || code.name == Opcode.OC_IF || code.name == Opcode.OC_ELSE)
1695                ++indent;
1696        }
1697        return buffer.toString();
1698    }
1699
1700    private void code(StringBuffer buffer, int indent, String code)
1701    {
1702        for (int i = 0; i < indent; ++i)
1703            buffer.append("\t");
1704        buffer.append(code);
1705        buffer.append("\n");
1706    }
1707
1708    public String pythonSource(String function)
1709    {
1710        StringBuffer buffer = new StringBuffer();
1711        int indent = 0;
1712
1713        if (function != null)
1714        {
1715            code(buffer, indent, "def " + function + "(**variables):");
1716            indent += 1;
1717        }
1718        code(buffer, indent, "import sys, marshal, datetime, itertools");
1719        code(buffer, indent, "from ll.misc import xmlescape");
1720        code(buffer, indent, "from ll import ul4c");
1721        code(buffer, indent, "source = u" + Utils.repr(source));
1722        code(buffer, indent, "variables = dict((key.decode('utf-8'), value) for (key, value) in variables.iteritems())");
1723
1724        int size = opcodes.size();
1725
1726        StringBuffer locations = new StringBuffer();
1727        StringBuffer lines2locs = new StringBuffer();
1728        int index = -1;
1729        Location lastLocation = null;
1730
1731        for (int i = 0; i < size; ++i)
1732        {
1733            Opcode opcode = (Opcode)opcodes.get(i);
1734           
1735            if (lastLocation != opcode.location)
1736            {
1737                if (locations.length()>0)
1738                    locations.append(", ");
1739
1740                lastLocation = opcode.location;
1741
1742                locations.append("(")
1743                         .append(Utils.repr(lastLocation.type))
1744                         .append(", ")
1745                         .append(lastLocation.starttag)
1746                         .append(", ")
1747                         .append(lastLocation.endtag)
1748                         .append(", ")
1749                         .append(lastLocation.startcode)
1750                         .append(", ")
1751                         .append(lastLocation.endcode)
1752                         .append(")");
1753                ++index;
1754            }
1755            if (lines2locs.length()>0)
1756                lines2locs.append(", ");
1757            lines2locs.append(index);
1758        }
1759        code(buffer, indent, "locations = (" + locations + ")");
1760        code(buffer, indent, "lines2locs = (" + lines2locs + ")");
1761
1762        code(buffer, indent, "reg0 = reg1 = reg2 = reg3 = reg4 = reg5 = reg6 = reg7 = reg8 = reg9 = None");
1763
1764        code(buffer, indent, "try:");
1765        indent += 1;
1766        code(buffer, indent, "startline = sys._getframe().f_lineno+1"); // The source line of the first opcode
1767
1768        int lastOpcode = -1;
1769        for (int i = 0; i < size; ++i)
1770        {
1771            Opcode opcode = (Opcode)opcodes.get(i);
1772       
1773            switch (opcode.name)
1774            {
1775                case Opcode.OC_TEXT:
1776                    code(buffer, indent, "yield u" + Utils.repr(opcode.location.getCode()));
1777                    break;
1778                case Opcode.OC_LOADSTR:
1779                    code(buffer, indent, "reg" + opcode.r1 + " = u" + Utils.repr(opcode.arg));
1780                    break;
1781                case Opcode.OC_LOADINT:
1782                    code(buffer, indent, "reg" + opcode.r1 + " = " + opcode.arg);
1783                    break;
1784                case Opcode.OC_LOADFLOAT:
1785                    code(buffer, indent, "reg" + opcode.r1 + " = " + opcode.arg);
1786                    break;
1787                case Opcode.OC_LOADNONE:
1788                    code(buffer, indent, "reg" + opcode.r1 + " = None");
1789                    break;
1790                case Opcode.OC_LOADFALSE:
1791                    code(buffer, indent, "reg" + opcode.r1 + " = False");
1792                    break;
1793                case Opcode.OC_LOADTRUE:
1794                    code(buffer, indent, "reg" + opcode.r1 + " = True");
1795                    break;
1796                case Opcode.OC_LOADDATE:
1797                    code(buffer, indent, "reg" + opcode.r1 + " = !!!");
1798                    break;
1799                case Opcode.OC_BUILDLIST:
1800                    code(buffer, indent, "reg" + opcode.r1 + " = []");
1801                    break;
1802                case Opcode.OC_BUILDDICT:
1803                    code(buffer, indent, "reg" + opcode.r1 + " = {}");
1804                    break;
1805                case Opcode.OC_ADDLIST:
1806                    code(buffer, indent, "reg" + opcode.r1 + ".append(reg" + opcode.r2 + ")");
1807                    break;
1808                case Opcode.OC_ADDDICT:
1809                    code(buffer, indent, "reg" + opcode.r1 + "[reg" + opcode.r2 + "] = reg" + opcode.r3);
1810                    break;
1811                case Opcode.OC_LOADVAR:
1812                    code(buffer, indent, "reg" + opcode.r1 + " = variables[u" + Utils.repr(opcode.arg) + "]");
1813                    break;
1814                case Opcode.OC_STOREVAR:
1815                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] = reg" + opcode.r1);
1816                    break;
1817                case Opcode.OC_ADDVAR:
1818                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] += reg" + opcode.r1);
1819                    break;
1820                case Opcode.OC_SUBVAR:
1821                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] -= reg" + opcode.r1);
1822                    break;
1823                case Opcode.OC_MULVAR:
1824                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] *= reg" + opcode.r1);
1825                    break;
1826                case Opcode.OC_TRUEDIVVAR:
1827                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] /= reg" + opcode.r1);
1828                    break;
1829                case Opcode.OC_FLOORDIVVAR:
1830                    code(buffer, indent, "variables[u" + Utils.repr(opcode.arg) + "] //= reg" + opcode.r1);
1831                    break;
1832                case Opcode.OC_DELVAR:
1833                    code(buffer, indent, "del variables[u" + Utils.repr(opcode.arg) + "]");
1834                    break;
1835                case Opcode.OC_GETATTR:
1836                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + "[u" + Utils.repr(opcode.arg) + "]");
1837                    break;
1838                case Opcode.OC_GETITEM:
1839                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + "[reg" + opcode.r3 + "]");
1840                    break;
1841                case Opcode.OC_GETSLICE12:
1842                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + "[reg" + opcode.r3 + ":reg" + opcode.r4 + "]");
1843                    break;
1844                case Opcode.OC_GETSLICE1:
1845                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + "[reg" + opcode.r3 + ":]");
1846                    break;
1847                case Opcode.OC_GETSLICE2:
1848                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + "[:reg" + opcode.r3 + "]");
1849                    break;
1850                case Opcode.OC_PRINT:
1851                    code(buffer, indent, "if reg" + opcode.r1 + " is not None: yield unicode(reg" + opcode.r1 + ")");
1852                    break;
1853                case Opcode.OC_PRINTX:
1854                    code(buffer, indent, "if reg" + opcode.r1 + " is not None: yield xmlescape(unicode(reg" + opcode.r1 + "))");
1855                    break;
1856                case Opcode.OC_FOR:
1857                    code(buffer, indent, "for reg" + opcode.r1 + " in reg" + opcode.r2 + ":");
1858                    indent += 1;
1859                    break;
1860                case Opcode.OC_ENDFOR:
1861                    indent -= 1;
1862                    code(buffer, indent, "# end for");
1863                    break;
1864                case Opcode.OC_BREAK:
1865                    code(buffer, indent, "break");
1866                    break;
1867                case Opcode.OC_CONTINUE:
1868                    code(buffer, indent, "continue");
1869                    break;
1870                case Opcode.OC_NOT:
1871                    code(buffer, indent, "reg" + opcode.r1 + " = not reg" + opcode.r2);
1872                    break;
1873                case Opcode.OC_NEG:
1874                    code(buffer, indent, "reg" + opcode.r1 + " = -reg" + opcode.r2);
1875                    break;
1876                case Opcode.OC_CONTAINS:
1877                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " in reg" + opcode.r3);
1878                    break;
1879                case Opcode.OC_NOTCONTAINS:
1880                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " not in reg" + opcode.r3);
1881                    break;
1882                case Opcode.OC_EQ:
1883                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " == reg" + opcode.r3);
1884                    break;
1885                case Opcode.OC_NE:
1886                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " != reg" + opcode.r3);
1887                    break;
1888                case Opcode.OC_LT:
1889                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " < reg" + opcode.r3);
1890                    break;
1891                case Opcode.OC_LE:
1892                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " <= reg" + opcode.r3);
1893                    break;
1894                case Opcode.OC_GT:
1895                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " > reg" + opcode.r3);
1896                    break;
1897                case Opcode.OC_GE:
1898                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " >= reg" + opcode.r3);
1899                    break;
1900                case Opcode.OC_ADD:
1901                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " + reg" + opcode.r3);
1902                    break;
1903                case Opcode.OC_SUB:
1904                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " - reg" + opcode.r3);
1905                    break;
1906                case Opcode.OC_MUL:
1907                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " * reg" + opcode.r3);
1908                    break;
1909                case Opcode.OC_FLOORDIV:
1910                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " // reg" + opcode.r3);
1911                    break;
1912                case Opcode.OC_TRUEDIV:
1913                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " / reg" + opcode.r3);
1914                    break;
1915                case Opcode.OC_AND:
1916                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " and reg" + opcode.r3);
1917                    break;
1918                case Opcode.OC_OR:
1919                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " or reg" + opcode.r3);
1920                    break;
1921                case Opcode.OC_MOD:
1922                    code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " % reg" + opcode.r3);
1923                    break;
1924                case Opcode.OC_CALLFUNC0:
1925                    switch (opcode.argcode)
1926                    {
1927                        case Opcode.CF0_NOW:
1928                            code(buffer, indent, "reg" + opcode.r1 + " = datetime.datetime.now()");
1929                            break;
1930                    }
1931                    break;
1932                case Opcode.OC_CALLFUNC1:
1933                    switch (opcode.argcode)
1934                    {
1935                        case Opcode.CF1_XMLESCAPE:
1936                            code(buffer, indent, "reg" + opcode.r1 + " = xmlescape(unicode(reg" + opcode.r2 + ")) if reg" + opcode.r2 + " is not None else u''");
1937                            break;
1938                        case Opcode.CF1_CSV:
1939                            code(buffer, indent, "reg" + opcode.r1 + " = ul4c._csv(reg" + opcode.r2 + ")");
1940                            break;
1941                        case Opcode.CF1_STR:
1942                            code(buffer, indent, "reg" + opcode.r1 + " = unicode(reg" + opcode.r2 + ") if reg" + opcode.r2 + " is not None else u''");
1943                            break;
1944                        case Opcode.CF1_INT:
1945                            code(buffer, indent, "reg" + opcode.r1 + " = int(reg" + opcode.r2 + ")");
1946                            break;
1947                        case Opcode.CF1_BOOL:
1948                            code(buffer, indent, "reg" + opcode.r1 + " = bool(reg" + opcode.r2 + ")");
1949                            break;
1950                        case Opcode.CF1_LEN:
1951                            code(buffer, indent, "reg" + opcode.r1 + " = len(reg" + opcode.r2 + ")");
1952                            break;
1953                        case Opcode.CF1_ENUMERATE:
1954                            code(buffer, indent, "reg" + opcode.r1 + " = enumerate(reg" + opcode.r2 + ")");
1955                            break;
1956                        case Opcode.CF1_ISNONE:
1957                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + " is not None");
1958                            break;
1959                        case Opcode.CF1_ISSTR:
1960                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", basestring)");
1961                            break;
1962                        case Opcode.CF1_ISINT:
1963                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", (int, long)) and not isinstance(reg" + opcode.r2 + ", bool)");
1964                            break;
1965                        case Opcode.CF1_ISFLOAT:
1966                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", float)");
1967                            break;
1968                        case Opcode.CF1_ISBOOL:
1969                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", bool)");
1970                            break;
1971                        case Opcode.CF1_ISDATE:
1972                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", datetime.datetime)");
1973                            break;
1974                        case Opcode.CF1_ISLIST:
1975                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", (list, tuple))");
1976                            break;
1977                        case Opcode.CF1_ISDICT:
1978                            code(buffer, indent, "reg" + opcode.r1 + " = isinstance(reg" + opcode.r2 + ", dict)");
1979                            break;
1980                        case Opcode.CF1_ISTEMPLATE:
1981                            code(buffer, indent, "reg" + opcode.r1 + " = hasattr(reg" + opcode.r2 + ", '__call__')");
1982                            break;
1983                        case Opcode.CF1_REPR:
1984                            code(buffer, indent, "reg" + opcode.r1 + " = ul4c._repr(reg" + opcode.r2 + ")");
1985                            break;
1986                        case Opcode.CF1_GET:
1987                            code(buffer, indent, "reg" + opcode.r1 + " = variables.get(reg" + opcode.r2 + ")");
1988                            break;
1989                        case Opcode.CF1_JSON:
1990                            code(buffer, indent, "reg" + opcode.r1 + " = json(reg" + opcode.r2 + ")");
1991                            break;
1992                        case Opcode.CF1_CHR:
1993                            code(buffer, indent, "reg" + opcode.r1 + " = unichr(reg" + opcode.r2 + ")");
1994                            break;
1995                        case Opcode.CF1_ORD:
1996                            code(buffer, indent, "reg" + opcode.r1 + " = ord(reg" + opcode.r2 + ")");
1997                            break;
1998                        case Opcode.CF1_HEX:
1999                            code(buffer, indent, "reg" + opcode.r1 + " = hex(reg" + opcode.r2 + ")");
2000                            break;
2001                        case Opcode.CF1_OCT:
2002                            code(buffer, indent, "reg" + opcode.r1 + " = ul4c._oct(reg" + opcode.r2 + ")");
2003                            break;
2004                        case Opcode.CF1_BIN:
2005                            code(buffer, indent, "reg" + opcode.r1 + " = ul4c._bin(reg" + opcode.r2 + ")");
2006                            break;
2007                        case Opcode.CF1_SORTED:
2008                            code(buffer, indent, "reg" + opcode.r1 + " = sorted(reg" + opcode.r2 + ")");
2009                            break;
2010                        case Opcode.CF1_RANGE:
2011                            code(buffer, indent, "reg" + opcode.r1 + " = xrange(reg" + opcode.r2 + ")");
2012                            break;
2013                    }
2014                    break;
2015                case Opcode.OC_CALLFUNC2:
2016                    switch (opcode.argcode)
2017                    {
2018                        case Opcode.CF2_RANGE:
2019                            code(buffer, indent, "reg" + opcode.r1 + " = xrange(reg" + opcode.r2 + ", reg" + opcode.r3 + ")");
2020                            break;
2021                        case Opcode.CF2_GET:
2022                            code(buffer, indent, "reg" + opcode.r1 + " = variables.get(reg" + opcode.r2 + ", reg" + opcode.r3 + ")");
2023                            break;
2024                        case Opcode.CF2_ZIP:
2025                            code(buffer, indent, "reg" + opcode.r1 + " = itertools.izip(reg" + opcode.r2 + ", reg" + opcode.r3 + ")");
2026                            break;
2027                        case Opcode.CF2_INT:
2028                            code(buffer, indent, "reg" + opcode.r1 + " = int(reg" + opcode.r2 + ", reg" + opcode.r3 + ")");
2029                            break;
2030                    }
2031                    break;
2032                case Opcode.OC_CALLFUNC3:
2033                    switch (opcode.argcode)
2034                    {
2035                        case Opcode.CF3_RANGE:
2036                            code(buffer, indent, "reg" + opcode.r1 + " = xrange(reg" + opcode.r2 + ", reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2037                            break;
2038                        case Opcode.CF3_ZIP:
2039                            code(buffer, indent, "reg" + opcode.r1 + " = itertools.izip(reg" + opcode.r2 + ", reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2040                            break;
2041                    }
2042                    break;
2043                case Opcode.OC_CALLMETH0:
2044                    switch (opcode.argcode)
2045                    {
2046                        case Opcode.CM0_SPLIT:
2047                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".split()");
2048                            break;
2049                        case Opcode.CM0_STRIP:
2050                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".strip()");
2051                            break;
2052                        case Opcode.CM0_LSTRIP:
2053                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".lstrip()");
2054                            break;
2055                        case Opcode.CM0_RSTRIP:
2056                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".rstrip()");
2057                            break;
2058                        case Opcode.CM0_UPPER:
2059                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".upper()");
2060                            break;
2061                        case Opcode.CM0_LOWER:
2062                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".lower()");
2063                            break;
2064                        case Opcode.CM0_CAPITALIZE:
2065                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".capitalize()");
2066                            break;
2067                        case Opcode.CM0_ISOFORMAT:
2068                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".isoformat()");
2069                            break;
2070                        case Opcode.CM0_ITEMS:
2071                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".iteritems()");
2072                            break;
2073                        case Opcode.CM0_HLS:
2074                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".hls()");
2075                            break;
2076                        case Opcode.CM0_HLSA:
2077                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".hlsa()");
2078                            break;
2079                        case Opcode.CM0_HSV:
2080                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".hsv()");
2081                            break;
2082                        case Opcode.CM0_HSVA:
2083                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".hsva()");
2084                            break;
2085                        case Opcode.CM0_LUM:
2086                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".lum()");
2087                            break;
2088                    }
2089                    break;
2090                case Opcode.OC_CALLMETH1:
2091                    switch (opcode.argcode)
2092                    {
2093                        case Opcode.CM1_SPLIT:
2094                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".split(reg" + opcode.r3 + ")");
2095                            break;
2096                        case Opcode.CM1_RSPLIT:
2097                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".rsplit(reg" + opcode.r3 + ")");
2098                            break;
2099                        case Opcode.CM1_STRIP:
2100                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".strip(reg" + opcode.r3 + ")");
2101                            break;
2102                        case Opcode.CM1_LSTRIP:
2103                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".lstrip(reg" + opcode.r3 + ")");
2104                            break;
2105                        case Opcode.CM1_RSTRIP:
2106                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".rstrip(reg" + opcode.r3 + ")");
2107                            break;
2108                        case Opcode.CM1_STARTSWITH:
2109                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".startswith(reg" + opcode.r3 + ")");
2110                            break;
2111                        case Opcode.CM1_ENDSWITH:
2112                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".endswith(reg" + opcode.r3 + ")");
2113                            break;
2114                        case Opcode.CM1_FIND:
2115                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".find(reg" + opcode.r3 + ")");
2116                            break;
2117                        case Opcode.CM1_GET:
2118                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".get(reg" + opcode.r3 + ")");
2119                            break;
2120                        case Opcode.CM1_FORMAT:
2121                            code(buffer, indent, "reg" + opcode.r1 + " = ul4c._format(reg" + opcode.r2 + ", reg" + opcode.r3 + ")");
2122                            break;
2123                        case Opcode.CM1_WITHLUM:
2124                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".withlum(reg" + opcode.r3 + ")");
2125                            break;
2126                        case Opcode.CM1_WITHA:
2127                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".witha(reg" + opcode.r3 + ")");
2128                            break;
2129                    }
2130                    break;
2131                case Opcode.OC_CALLMETH2:
2132                    switch (opcode.argcode)
2133                    {
2134                        case Opcode.CM2_SPLIT:
2135                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".split(reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2136                            break;
2137                        case Opcode.CM2_RSPLIT:
2138                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".rsplit(reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2139                            break;
2140                        case Opcode.CM2_FIND:
2141                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".find(reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2142                            break;
2143                        case Opcode.CM2_REPLACE:
2144                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".replace(reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2145                            break;
2146                        case Opcode.CM2_GET:
2147                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".get(reg" + opcode.r3 + ", reg" + opcode.r4 + ")");
2148                            break;
2149                    }
2150                    break;
2151                case Opcode.OC_CALLMETH3:
2152                    switch (opcode.argcode)
2153                    {
2154                        case Opcode.CM3_FIND:
2155                            code(buffer, indent, "reg" + opcode.r1 + " = reg" + opcode.r2 + ".find(reg" + opcode.r3 + ", reg" + opcode.r4 + ", reg" + opcode.r5 + ")");
2156                            break;
2157                    }
2158                    break;
2159                case Opcode.OC_CALLMETHKW:
2160                    switch (opcode.argcode)
2161                    {
2162                        case Opcode.CMKW_RENDER:
2163                            code(buffer, indent, "reg" + opcode.r1 + " = ''.join(reg" + opcode.r2 + "(**dict((key.encode(\"utf-8\"), value) for (key, value) in reg" + opcode.r3 + ".iteritems())))");
2164                            break;
2165                    }
2166                    break;
2167                case Opcode.OC_IF:
2168                    code(buffer, indent, "if reg" + opcode.r1 + ":");
2169                    indent += 1;
2170                    break;
2171                case Opcode.OC_ELSE:
2172                    if (lastOpcode == Opcode.OC_IF)
2173                        buffer.insert(buffer.length()-1, " pass");
2174                    indent -= 1;
2175                    code(buffer, indent, "else:");
2176                    indent += 1;
2177                    break;
2178                case Opcode.OC_ENDIF:
2179                    if (lastOpcode == Opcode.OC_IF || lastOpcode == Opcode.OC_ELSE)
2180                        buffer.insert(buffer.length()-1, " pass");
2181                    indent -= 1;
2182                    code(buffer, indent, "# end if");
2183                    break;
2184                case Opcode.OC_RENDER:
2185                    code(buffer, indent, "for chunk in reg" + opcode.r1 + "(**dict((key.encode('utf-8'), value) for (key, value) in reg" + opcode.r2 + ".iteritems())): yield chunk");
2186                    break;
2187            }
2188            lastOpcode = opcode.name;
2189        }
2190        indent -= 1;
2191        code(buffer, indent, "except Exception, exc:");
2192        indent += 1;
2193        code(buffer, indent, "raise ul4c.Error(ul4c.Location(source, *locations[lines2locs[sys.exc_info()[2].tb_lineno-startline]]), exc)");
2194        return buffer.toString();
2195    }
2196
2197    public void renderjsp(JspWriter out, Map variables) throws java.io.IOException
2198    {
2199        for (Iterator iterator = render(variables); iterator.hasNext();)
2200        {
2201            out.write((String)iterator.next());
2202        }
2203    }
2204}
Note: See TracBrowser for help on using the browser.