a clean restart

--HG--
branch : trunk
rename : jinja/__init__.py => jinja2/__init__.py
rename : jinja/_debugger.c => jinja2/_debugger.c
rename : jinja/_native.py => jinja2/_native.py
rename : jinja/_speedups.c => jinja2/_speedups.c
rename : jinja/constants.py => jinja2/constants.py
rename : jinja/contrib/__init__.py => jinja2/contrib/__init__.py
rename : jinja/contrib/_djangosupport.py => jinja2/contrib/_djangosupport.py
rename : jinja/contrib/djangosupport.py => jinja2/contrib/djangosupport.py
rename : jinja/datastructure.py => jinja2/datastructure.py
rename : jinja/defaults.py => jinja2/defaults.py
rename : jinja/environment.py => jinja2/environment.py
rename : jinja/exceptions.py => jinja2/exceptions.py
rename : jinja/filters.py => jinja2/filters.py
rename : jinja/lexer.py => jinja2/lexer.py
rename : jinja/loaders.py => jinja2/loaders.py
rename : jinja/nodes.py => jinja2/nodes.py
rename : jinja/parser.py => jinja2/parser.py
rename : jinja/tests.py => jinja2/tests.py
rename : jinja/translators/__init__.py => jinja2/translators/__init__.py
rename : jinja/translators/python.py => jinja2/translators/python.py
rename : jinja/utils.py => jinja2/utils.py
diff --git a/jinja2/__init__.py b/jinja2/__init__.py
new file mode 100644
index 0000000..94949c1
--- /dev/null
+++ b/jinja2/__init__.py
@@ -0,0 +1,58 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2
+    ~~~~~~
+
+    Jinja is a `sandboxed`_ template engine written in pure Python. It
+    provides a `Django`_ like non-XML syntax and compiles templates into
+    executable python code. It's basically a combination of Django templates
+    and python code.
+
+    Nutshell
+    --------
+
+    Here a small example of a Jinja template::
+
+        {% extends 'base.html' %}
+        {% block title %}Memberlist{% endblock %}
+        {% block content %}
+          <ul>
+          {% for user in users %}
+            <li><a href="{{ user.url|e }}">{{ user.username|e }}</a></li>
+          {% endfor %}
+          </ul>
+        {% endblock %}
+
+    Philosophy
+    ----------
+
+    Application logic is for the controller but don't try to make the life
+    for the template designer too hard by giving him too few functionality.
+
+    For more informations visit the new `jinja webpage`_ and `documentation`_.
+
+    Note
+    ----
+
+    This is the Jinja 1.0 release which is completely incompatible with the
+    old "pre 1.0" branch. The old branch will still receive security updates
+    and bugfixes but the 1.0 branch will be the only version that receives
+    support.
+
+    If you have an application that uses Jinja 0.9 and won't be updated in
+    the near future the best idea is to ship a Jinja 0.9 checkout together
+    with the application.
+
+    The `Jinja tip`_ is installable via `easy_install` with ``easy_install
+    Jinja==dev``.
+
+    .. _sandboxed: http://en.wikipedia.org/wiki/Sandbox_(computer_security)
+    .. _Django: http://www.djangoproject.com/
+    .. _jinja webpage: http://jinja.pocoo.org/
+    .. _documentation: http://jinja.pocoo.org/documentation/index.html
+    .. _Jinja tip: http://dev.pocoo.org/hg/jinja-main/archive/tip.tar.gz#egg=Jinja-dev
+
+
+    :copyright: 2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/jinja2/_debugger.c b/jinja2/_debugger.c
new file mode 100644
index 0000000..50462e1
--- /dev/null
+++ b/jinja2/_debugger.c
@@ -0,0 +1,65 @@
+/**
+ * Jinja Extended Debugger
+ * ~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * this module allows the jinja debugger to set the tb_next flag
+ * on traceback objects. This is required to inject a traceback into
+ * another one.
+ *
+ * For better windows support (not everybody has a visual studio 2003
+ * at home) it would be a good thing to have a ctypes implementation, but
+ * because the struct is not exported there is currently no sane way.
+ *
+ * :copyright: 2007 by Armin Ronacher.
+ * :license: BSD, see LICENSE for more details.
+ */
+
+#include <Python.h>
+
+
+/**
+ * set the tb_next attribute of a traceback object
+ */
+static PyObject *
+tb_set_next(PyObject *self, PyObject *args)
+{
+	PyTracebackObject *tb, *old;
+	PyObject *next;
+
+	if (!PyArg_ParseTuple(args, "O!O:tb_set_next", &PyTraceBack_Type, &tb, &next))
+		return NULL;
+	if (next == Py_None)
+		next = NULL;
+	else if (!PyTraceBack_Check(next)) {
+		PyErr_SetString(PyExc_TypeError,
+				"tb_set_next arg 2 must be traceback or None");
+		return NULL;
+	}
+	else
+		Py_INCREF(next);
+
+	old = tb->tb_next;
+	tb->tb_next = (PyTracebackObject*)next;
+	Py_XDECREF(old);
+
+	Py_INCREF(Py_None);
+	return Py_None;
+}
+
+
+static PyMethodDef module_methods[] = {
+	{"tb_set_next", (PyCFunction)tb_set_next, METH_VARARGS,
+	 "Set the tb_next member of a traceback object."},
+	{NULL, NULL, 0, NULL}		/* Sentinel */
+};
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_debugger(void)
+{
+	PyObject *module = Py_InitModule3("jinja._debugger", module_methods, "");
+	if (!module)
+		return;
+}
diff --git a/jinja2/_native.py b/jinja2/_native.py
new file mode 100644
index 0000000..1d9747a
--- /dev/null
+++ b/jinja2/_native.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja._native
+    ~~~~~~~~~~~~~
+
+    This module implements the native base classes in case of not
+    having a jinja with the _speedups module compiled.
+
+    Note that if you change semantics here you have to edit the
+    _speedups.c file to in order to support those changes for jinja
+    setups with enabled speedup module.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja.datastructure import Deferred
+from jinja.utils import deque
+
+
+class BaseContext(object):
+
+    def __init__(self, undefined_singleton, globals, initial):
+        self._undefined_singleton = undefined_singleton
+        self.current = current = {}
+        self._stack = deque([current, initial, globals])
+        self.globals = globals
+        self.initial = initial
+
+        self._push = self._stack.appendleft
+        self._pop = self._stack.popleft
+
+    def stack(self):
+        return list(self._stack)[::-1]
+    stack = property(stack)
+
+    def pop(self):
+        """Pop the last layer from the stack and return it."""
+        rv = self._pop()
+        self.current = self._stack[0]
+        return rv
+
+    def push(self, data=None):
+        """
+        Push one layer to the stack and return it. Layer must be
+        a dict or omitted.
+        """
+        data = data or {}
+        self._push(data)
+        self.current = self._stack[0]
+        return data
+
+    def __getitem__(self, name):
+        """
+        Resolve one item. Restrict the access to internal variables
+        such as ``'::cycle1'``. Resolve deferreds.
+        """
+        if not name.startswith('::'):
+            for d in self._stack:
+                if name in d:
+                    rv = d[name]
+                    if rv.__class__ is Deferred:
+                        rv = rv(self, name)
+                        # never touch the globals!
+                        if d is self.globals:
+                            self.initial[name] = rv
+                        else:
+                            d[name] = rv
+                    return rv
+        return self._undefined_singleton
+
+    def __setitem__(self, name, value):
+        """Set a variable in the outermost layer."""
+        self.current[name] = value
+
+    def __delitem__(self, name):
+        """Delete a variable in the outermost layer."""
+        if name in self.current:
+            del self.current[name]
+
+    def __contains__(self, name):
+        """ Check if the context contains a given variable."""
+        for layer in self._stack:
+            if name in layer:
+                return True
+        return False
diff --git a/jinja2/_speedups.c b/jinja2/_speedups.c
new file mode 100644
index 0000000..198b7a0
--- /dev/null
+++ b/jinja2/_speedups.c
@@ -0,0 +1,499 @@
+/**
+ * jinja._speedups
+ * ~~~~~~~~~~~~~~~
+ *
+ * This module implements the BaseContext, a c implementation of the
+ * Context baseclass. If this extension is not compiled the datastructure
+ * module implements a class in python.
+ *
+ * Note that if you change semantics here you have to edit the _native.py
+ * to in order to support those changes for jinja setups without the
+ * speedup module too.
+ *
+ * :copyright: 2007 by Armin Ronacher.
+ * :license: BSD, see LICENSE for more details.
+ */
+
+#include <Python.h>
+#include <structmember.h>
+
+/* Set by init_constants to real values */
+static PyObject *Deferred;
+
+/**
+ * Internal struct used by BaseContext to store the
+ * stacked namespaces.
+ */
+struct StackLayer {
+	PyObject *dict;			/* current value, a dict */
+	struct StackLayer *prev;	/* lower struct layer or NULL */
+};
+
+/**
+ * BaseContext python class.
+ */
+typedef struct {
+	PyObject_HEAD
+	struct StackLayer *globals;	/* the dict for the globals */
+	struct StackLayer *initial;	/* initial values */
+	struct StackLayer *current;	/* current values */
+	long stacksize;			/* current size of the stack */
+	PyObject *undefined_singleton;	/* the singleton returned on missing values */
+} BaseContext;
+
+/**
+ * Called by init_speedups in order to retrieve references
+ * to some exceptions and classes defined in jinja python modules
+ */
+static int
+init_constants(void)
+{
+	PyObject *datastructure = PyImport_ImportModule("jinja.datastructure");
+	if (!datastructure)
+		return 0;
+	Deferred = PyObject_GetAttrString(datastructure, "Deferred");
+	Py_DECREF(datastructure);
+	return 1;
+}
+
+/**
+ * GC Helper
+ */
+static int
+BaseContext_clear(BaseContext *self)
+{
+	struct StackLayer *current = self->current, *tmp;
+	while (current) {
+		tmp = current;
+		Py_XDECREF(current->dict);
+		current->dict = NULL;
+		current = tmp->prev;
+		PyMem_Free(tmp);
+	}
+	self->current = NULL;
+	return 0;
+}
+
+/**
+ * Deallocator for BaseContext.
+ *
+ * Frees the memory for the stack layers before freeing the object.
+ */
+static void
+BaseContext_dealloc(BaseContext *self)
+{
+	BaseContext_clear(self);
+	self->ob_type->tp_free((PyObject*)self);
+}
+
+/**
+ * GC Helper
+ */
+static int
+BaseContext_traverse(BaseContext *self, visitproc visit, void *args)
+{
+	int vret;
+	struct StackLayer *layer = self->current;
+
+	while (layer) {
+		vret = visit(layer->dict, args);
+		if (vret != 0)
+			return vret;
+		layer = layer->prev;
+	}
+
+	return 0;
+}
+
+/**
+ * Initializes the BaseContext.
+ *
+ * Like the native python class it takes a reference to the undefined
+ * singleton which will be used for undefined values.
+ * The other two arguments are the global namespace and the initial
+ * namespace which usually contains the values passed to the render
+ * function of the template. Both must be dicts.
+ */
+static int
+BaseContext_init(BaseContext *self, PyObject *args, PyObject *kwds)
+{
+	PyObject *undefined = NULL, *globals = NULL, *initial = NULL;
+
+	if (!PyArg_ParseTuple(args, "OOO", &undefined, &globals, &initial))
+		return -1;
+	if (!PyDict_Check(globals) || !PyDict_Check(initial)) {
+		PyErr_SetString(PyExc_TypeError, "stack layers must be dicts.");
+		return -1;
+	}
+
+	self->current = PyMem_Malloc(sizeof(struct StackLayer));
+	self->current->prev = NULL;
+	self->current->dict = PyDict_New();
+	if (!self->current->dict)
+		return -1;
+
+	self->initial = PyMem_Malloc(sizeof(struct StackLayer));
+	self->initial->prev = NULL;
+	self->initial->dict = initial;
+	Py_INCREF(initial);
+	self->current->prev = self->initial;
+
+	self->globals = PyMem_Malloc(sizeof(struct StackLayer));
+	self->globals->prev = NULL;
+	self->globals->dict = globals;
+	Py_INCREF(globals);
+	self->initial->prev = self->globals;
+
+	self->undefined_singleton = undefined;
+	Py_INCREF(undefined);
+
+	self->stacksize = 3;
+	return 0;
+}
+
+/**
+ * Pop the highest layer from the stack and return it
+ */
+static PyObject*
+BaseContext_pop(BaseContext *self)
+{
+	PyObject *result;
+	struct StackLayer *tmp = self->current;
+
+	if (self->stacksize <= 3) {
+		PyErr_SetString(PyExc_IndexError, "stack too small.");
+		return NULL;
+	}
+	result = self->current->dict;
+	assert(result);
+	self->current = tmp->prev;
+	PyMem_Free(tmp);
+	self->stacksize--;
+	/* Took the reference to result from the struct. */
+	return result;
+}
+
+/**
+ * Push a new layer to the stack and return it. If no parameter
+ * is provided an empty dict is created. Otherwise the dict passed
+ * to it is used as new layer.
+ */
+static PyObject*
+BaseContext_push(BaseContext *self, PyObject *args)
+{
+	PyObject *value = NULL;
+	struct StackLayer *new;
+
+	if (!PyArg_ParseTuple(args, "|O:push", &value))
+		return NULL;
+	if (!value) {
+		value = PyDict_New();
+		if (!value)
+			return NULL;
+	}
+	else if (!PyDict_Check(value)) {
+		PyErr_SetString(PyExc_TypeError, "dict required.");
+		return NULL;
+	}
+	else
+		Py_INCREF(value);
+	new = PyMem_Malloc(sizeof(struct StackLayer));
+	if (!new) {
+		Py_DECREF(value);
+		return NULL;
+	}
+	new->dict = value;
+	new->prev = self->current;
+	self->current = new;
+	self->stacksize++;
+	Py_INCREF(value);
+	return value;
+}
+
+/**
+ * Getter that creates a list representation of the internal
+ * stack. Used for compatibility with the native python implementation.
+ */
+static PyObject*
+BaseContext_getstack(BaseContext *self, void *closure)
+{
+	int idx = 0;
+	struct StackLayer *current = self->current;
+	PyObject *result = PyList_New(self->stacksize);
+	if (!result)
+		return NULL;
+	while (current) {
+		Py_INCREF(current->dict);
+		PyList_SET_ITEM(result, idx++, current->dict);
+		current = current->prev;
+	}
+	PyList_Reverse(result);
+	return result;
+}
+
+/**
+ * Getter that returns a reference to the current layer in the context.
+ */
+static PyObject*
+BaseContext_getcurrent(BaseContext *self, void *closure)
+{
+	Py_INCREF(self->current->dict);
+	return self->current->dict;
+}
+
+/**
+ * Getter that returns a reference to the initial layer in the context.
+ */
+static PyObject*
+BaseContext_getinitial(BaseContext *self, void *closure)
+{
+	Py_INCREF(self->initial->dict);
+	return self->initial->dict;
+}
+
+/**
+ * Getter that returns a reference to the global layer in the context.
+ */
+static PyObject*
+BaseContext_getglobals(BaseContext *self, void *closure)
+{
+	Py_INCREF(self->globals->dict);
+	return self->globals->dict;
+}
+
+/**
+ * Implements the context lookup.
+ *
+ * This works exactly like the native implementation but a lot
+ * faster. It disallows access to internal names (names that start
+ * with "::") and resolves Deferred values.
+ */
+static PyObject*
+BaseContext_getitem(BaseContext *self, PyObject *item)
+{
+	PyObject *result;
+	char *name = NULL;
+	int isdeferred;
+	struct StackLayer *current = self->current;
+	
+	/* allow unicode keys as long as they are ascii keys */
+	if (PyUnicode_CheckExact(item)) {
+		item = PyUnicode_AsASCIIString(item);
+		if (!item)
+			goto missing;
+	}
+	else if (!PyString_Check(item))
+		goto missing;
+
+	/* disallow access to internal jinja values */
+	name = PyString_AS_STRING(item);
+	if (name[0] == ':' && name[1] == ':')
+		goto missing;
+
+	while (current) {
+		/* GetItemString just builds a new string from "name" again... */
+		result = PyDict_GetItem(current->dict, item);
+		if (!result) {
+			current = current->prev;
+			continue;
+		}
+		isdeferred = PyObject_IsInstance(result, Deferred);
+		if (isdeferred == -1)
+			return NULL;
+		else if (isdeferred) {
+			PyObject *namespace;
+			PyObject *resolved = PyObject_CallFunctionObjArgs(
+						result, self, item, NULL);
+			if (!resolved)
+				return NULL;
+
+			/* never touch the globals */
+			if (current == self->globals)
+				namespace = self->initial->dict;
+			else
+				namespace = current->dict;
+			if (PyDict_SetItem(namespace, item, resolved) < 0)
+				return NULL;
+			Py_INCREF(resolved);
+			return resolved;
+		}
+		Py_INCREF(result);
+		return result;
+	}
+
+missing:
+	Py_INCREF(self->undefined_singleton);
+	return self->undefined_singleton;
+}
+
+/**
+ * Check if the context contains a given value.
+ */
+static int
+BaseContext_contains(BaseContext *self, PyObject *item)
+{
+	char *name;
+	struct StackLayer *current = self->current;
+
+	/* allow unicode objects as keys as long as they are ASCII */
+	if (PyUnicode_CheckExact(item)) {
+		item = PyUnicode_AsASCIIString(item);
+		if (!item)
+			return 0;
+	}
+	else if (!PyString_Check(item))
+		return 0;
+
+	name = PyString_AS_STRING(item);
+	if (name[0] == ':' && name[1] == ':')
+		return 0;
+
+	while (current) {
+		/* XXX: for 2.4 and newer, use PyDict_Contains */
+		if (!PyMapping_HasKey(current->dict, item)) {
+			current = current->prev;
+			continue;
+		}
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Set an value in the highest layer or delete one.
+ */
+static int
+BaseContext_setitem(BaseContext *self, PyObject *item, PyObject *value)
+{
+	/* allow unicode objects as keys as long as they are ASCII */
+	if (PyUnicode_CheckExact(item)) {
+		item = PyUnicode_AsASCIIString(item);
+		if (!item) {
+			PyErr_Clear();
+			goto error;
+		}
+	}
+	else if (!PyString_Check(item))
+		goto error;
+	if (!value)
+		return PyDict_DelItem(self->current->dict, item);
+	return PyDict_SetItem(self->current->dict, item, value);
+
+error:
+	PyErr_SetString(PyExc_TypeError, "expected string argument");
+	return -1;
+}
+
+
+static PyGetSetDef BaseContext_getsetters[] = {
+	{"stack", (getter)BaseContext_getstack, NULL,
+	 "a read only copy of the internal stack", NULL},
+	{"current", (getter)BaseContext_getcurrent, NULL,
+	 "reference to the current layer on the stack", NULL},
+	{"initial", (getter)BaseContext_getinitial, NULL,
+	 "reference to the initial layer on the stack", NULL},
+	{"globals", (getter)BaseContext_getglobals, NULL,
+	 "reference to the global layer on the stack", NULL},
+	{NULL}				/* Sentinel */
+};
+
+static PyMethodDef BaseContext_methods[] = {
+	{"pop", (PyCFunction)BaseContext_pop, METH_NOARGS,
+	 "ctx.pop() -> dict\n\n"
+	 "Pop the last layer from the stack and return it."},
+	{"push", (PyCFunction)BaseContext_push, METH_VARARGS,
+	 "ctx.push([layer]) -> layer\n\n"
+	 "Push one layer to the stack. Layer must be a dict "
+	 "or omitted."},
+	{NULL}				/* Sentinel */
+};
+
+static PySequenceMethods BaseContext_as_sequence = {
+	0,				/* sq_length */
+	0,				/* sq_concat */
+	0,				/* sq_repeat */
+	0,				/* sq_item */
+	0,				/* sq_slice */
+	0,				/* sq_ass_item */
+	0,				/* sq_ass_slice */
+	(objobjproc)BaseContext_contains,/* sq_contains */
+	0,				/* sq_inplace_concat */
+	0				/* sq_inplace_repeat */
+};
+
+static PyMappingMethods BaseContext_as_mapping = {
+	NULL,
+	(binaryfunc)BaseContext_getitem,
+	(objobjargproc)BaseContext_setitem
+};
+
+static PyTypeObject BaseContextType = {
+	PyObject_HEAD_INIT(NULL)
+	0,				/* ob_size */
+	"jinja._speedups.BaseContext",	/* tp_name */
+	sizeof(BaseContext),		/* tp_basicsize */
+	0,				/* tp_itemsize */
+	(destructor)BaseContext_dealloc,/* tp_dealloc */
+	0,				/* tp_print */
+	0,				/* tp_getattr */
+	0,				/* tp_setattr */
+	0,				/* tp_compare */
+	0,				/* tp_repr */
+	0,				/* tp_as_number */
+	&BaseContext_as_sequence,	/* tp_as_sequence */
+	&BaseContext_as_mapping,	/* tp_as_mapping */
+	0,				/* tp_hash */
+	0,				/* tp_call */
+	0,				/* tp_str */
+	0,				/* tp_getattro */
+	0,				/* tp_setattro */
+	0,				/* tp_as_buffer */
+	Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
+					/*tp_flags*/
+	"",				/* tp_doc */
+	(traverseproc)BaseContext_traverse, /* tp_traverse */
+	(inquiry)BaseContext_clear,	/* tp_clear */
+	0,				/* tp_richcompare */
+	0,				/* tp_weaklistoffset */
+	0,				/* tp_iter */
+	0,				/* tp_iternext */
+	BaseContext_methods,		/* tp_methods */
+	0,				/* tp_members */
+	BaseContext_getsetters,		/* tp_getset */
+	0,				/* tp_base */
+	0,				/* tp_dict */
+	0,				/* tp_descr_get */
+	0,				/* tp_descr_set */
+	0,				/* tp_dictoffset */
+	(initproc)BaseContext_init,	/* tp_init */
+	0,				/* tp_alloc */
+	0				/* tp_new */
+};
+
+static PyMethodDef module_methods[] = {
+	{NULL, NULL, 0, NULL}		/* Sentinel */
+};
+
+#ifndef PyMODINIT_FUNC	/* declarations for DLL import/export */
+#define PyMODINIT_FUNC void
+#endif
+PyMODINIT_FUNC
+init_speedups(void)
+{
+	PyObject *module;
+
+	BaseContextType.tp_new = (newfunc)PyType_GenericNew;
+	if (PyType_Ready(&BaseContextType) < 0)
+		return;
+
+	if (!init_constants())
+		return;
+
+	module = Py_InitModule3("_speedups", module_methods, "");
+	if (!module)
+		return;
+
+	Py_INCREF(&BaseContextType);
+	PyModule_AddObject(module, "BaseContext", (PyObject*)&BaseContextType);
+}
diff --git a/jinja2/constants.py b/jinja2/constants.py
new file mode 100644
index 0000000..a0b4a63
--- /dev/null
+++ b/jinja2/constants.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.constants
+    ~~~~~~~~~~~~~~~
+
+    Various constants.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+
+
+#: list of lorem ipsum words used by the lipsum() helper function
+LOREM_IPSUM_WORDS = u'''\
+a ac accumsan ad adipiscing aenean aliquam aliquet amet ante aptent arcu at
+auctor augue bibendum blandit class commodo condimentum congue consectetuer
+consequat conubia convallis cras cubilia cum curabitur curae cursus dapibus
+diam dictum dictumst dignissim dis dolor donec dui duis egestas eget eleifend
+elementum elit enim erat eros est et etiam eu euismod facilisi facilisis fames
+faucibus felis fermentum feugiat fringilla fusce gravida habitant habitasse hac
+hendrerit hymenaeos iaculis id imperdiet in inceptos integer interdum ipsum
+justo lacinia lacus laoreet lectus leo libero ligula litora lobortis lorem
+luctus maecenas magna magnis malesuada massa mattis mauris metus mi molestie
+mollis montes morbi mus nam nascetur natoque nec neque netus nibh nisi nisl non
+nonummy nostra nulla nullam nunc odio orci ornare parturient pede pellentesque
+penatibus per pharetra phasellus placerat platea porta porttitor posuere
+potenti praesent pretium primis proin pulvinar purus quam quis quisque rhoncus
+ridiculus risus rutrum sagittis sapien scelerisque sed sem semper senectus sit
+sociis sociosqu sodales sollicitudin suscipit suspendisse taciti tellus tempor
+tempus tincidunt torquent tortor tristique turpis ullamcorper ultrices
+ultricies urna ut varius vehicula vel velit venenatis vestibulum vitae vivamus
+viverra volutpat vulputate'''
diff --git a/jinja2/contrib/__init__.py b/jinja2/contrib/__init__.py
new file mode 100644
index 0000000..1770052
--- /dev/null
+++ b/jinja2/contrib/__init__.py
@@ -0,0 +1,11 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.contrib
+    ~~~~~~~~~~~~~
+
+    This module collections various third-party helper functions and classes
+    that are useful for frameworks etc.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/jinja2/contrib/_djangosupport.py b/jinja2/contrib/_djangosupport.py
new file mode 100644
index 0000000..65a192e
--- /dev/null
+++ b/jinja2/contrib/_djangosupport.py
@@ -0,0 +1,209 @@
+# -*- coding: utf-8 -*-

+"""

+    jinja.contrib._djangosupport

+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~

+

+    Django suport layer. This module is a metamodule, do never import it

+    directly or access any of the functions defined here.

+

+    The public interface is `jinja.contrib.djangosupport` and

+    `django.contrib.jinja`. See the docstring of `jinja.contrib.djangosupport`

+    for more details.

+

+    :copyright: 2007-2008 by Armin Ronacher, Bryan McLemore, David Cramer.

+    :license: BSD, see LICENSE for more details.

+"""

+import sys

+import warnings

+import new

+from django.conf import settings

+from django.template.context import get_standard_processors

+from django.http import HttpResponse

+from django import contrib

+

+from jinja import Environment, FileSystemLoader, ChoiceLoader

+from jinja.loaders import MemcachedFileSystemLoader

+

+

+exported = ['render_to_response', 'render_to_string', 'convert_django_filter']

+

+

+#: used environment

+env = None

+

+

+#: default filters

+DEFAULT_FILTERS = (

+    'django.template.defaultfilters.date',

+    'django.template.defaultfilters.timesince',

+    'django.template.defaultfilters.linebreaks',

+    'django.contrib.humanize.templatetags.humanize.intcomma'

+)

+

+def configure(convert_filters=DEFAULT_FILTERS, loader=None, **options):

+    """

+    Initialize the system.

+    """

+    global env

+

+    if env:

+        warnings.warn("Jinja already initialized.")

+        return

+

+    # setup environment

+    if loader is None:

+        loaders = tuple(FileSystemLoader(l) for l in settings.TEMPLATE_DIRS)

+        if not loaders:

+            loader = None

+        elif len(loaders) == 1:

+            loader = loaders[0]

+        else:

+            loader = ChoiceLoader(loaders)

+    env = Environment(loader=loader, **options)

+

+    # convert requested filters

+    for name in convert_filters:

+        env.filters[name] = convert_django_filter(name)

+

+    # import templatetags of installed apps

+    for app in settings.INSTALLED_APPS:

+        try:

+            __import__(app + '.templatetags')

+        except ImportError:

+            pass

+

+    # setup the django.contrib.jinja module

+    setup_django_module()

+

+

+def setup_django_module():

+    """

+    create a new Jinja module for django.

+    """

+    from jinja.contrib import djangosupport

+    module = contrib.jinja = sys.modules['django.contrib.jinja'] = \

+             new.module('django.contrib.jinja')

+    module.env = env

+    module.__doc__ = djangosupport.__doc__

+    module.register = Library

+    public_names = module.__all__ = ['register', 'env']

+    get_name = globals().get

+    for name in exported:

+        setattr(module, name, get_name(name))

+        public_names.append(name)

+

+

+def render_to_response(template, context={}, request=None,

+                       mimetype=None):

+    """This function will take a few variables and spit out a full webpage."""

+    content = render_to_string(template, context, request)

+    if mimetype is None:

+        mimetype = settings.DEFAULT_CONTENT_TYPE

+    return HttpResponse(content, mimetype)

+

+

+def render_to_string(template, context={}, request=None):

+    """Render a template to a string."""

+    assert env is not None, 'Jinja not configured for django'

+    if request is not None:

+        context['request'] = request

+        for processor in get_standard_processors():

+            context.update(processor(request))

+    template = env.get_template(template)

+    return template.render(context)

+

+

+def convert_django_filter(f):

+    """Convert a django filter into a Jinja filter."""

+    if isinstance(f, str):

+        p = f.split('.')

+        f = getattr(__import__('.'.join(p[:-1]), None, None, ['']), p[-1])

+    def filter_factory(*args):

+        def wrapped(env, ctx, value):

+            return f(value, *args)

+        return wrapped

+    filter_factory.__name__ = f.__name__

+    filter_factory.__doc__ = getattr(f, '__doc__', None)

+    return filter_factory

+

+

+class Library(object):

+    """

+    Continues a general feel of wrapping all the registration

+    methods for easy importing.

+

+    This is available in `django.contrib.jinja` as `register`.

+

+    For more details see the docstring of the `django.contrib.jinja` module.

+    """

+    def object(func, name=None):

+        """Register a new global."""

+        if name is None:

+            name = getattr(func, '__name__')

+        env.globals[name] = func

+        return func

+    object = staticmethod(object)

+

+    def filter(func, name=None):

+        """Register a new filter function."""

+        if name is None:

+            name = func.__name__

+        env.filters[name] = func

+        return func

+    filter = staticmethod(filter)

+

+    def test(func, name):

+        """Register a new test function."""

+        if name is None:

+            name = func.__name__

+        env.tests[name] = func

+        return func

+    test = staticmethod(test)

+

+    def context_inclusion(func, template, name=None):

+        """

+        Similar to the inclusion tag from django this one expects func to be a

+        function with a similar argument list to func(context, *args, **kwargs)

+

+        It passed in the current context allowing the function to edit it or read

+        from it.  the function must return a dict with which to pass into the

+        renderer.  Normally expected is an altered dictionary.

+

+        Note processors are NOT ran on this context.

+        """

+        def wrapper(env, context, *args, **kwargs):

+            context = func(context.to_dict(), *args, **kwargs)

+            return render_to_string(template, context)

+        wrapper.jinja_context_callable = True

+        if name is None:

+            name = func.__name__

+        try:

+            wrapper.__name__ = func.__name__

+            wrapper.__doc__ = func.__doc__

+        except:

+            pass

+        env.globals[name] = wrapper

+    context_inclusion = staticmethod(context_inclusion)

+

+    def clean_inclusion(func, template, name=None, run_processors=False):

+        """

+        Similar to above however it won't pass the context into func().

+        Also the returned context will have the context processors run upon it.

+        """

+        def wrapper(env, context, *args, **kwargs):

+            if run_processors:

+                request = context['request']

+            else:

+                request = None

+            context = func({}, *args, **kwargs)

+            return render_to_string(template, context, request)

+        wrapper.jinja_context_callable = True

+        if name is None:

+            name = func.__name__

+        try:

+            wrapper.__name__ = func.__name__

+            wrapper.__doc__ = func.__doc__

+        except:

+            pass

+        env.globals[name] = wrapper

+    clean_inclusion = staticmethod(clean_inclusion)

diff --git a/jinja2/contrib/djangosupport.py b/jinja2/contrib/djangosupport.py
new file mode 100644
index 0000000..9898a82
--- /dev/null
+++ b/jinja2/contrib/djangosupport.py
@@ -0,0 +1,124 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.contrib.djangosupport
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Support for the django framework. This module is quite magical because it
+    just exports one single function, the `configure` function which is used
+    to create a new Jinja environment and setup a special module called
+    `django.contrib.jinja` which exports a couple of functions useful for Jinja.
+
+    Quickstart
+    ==========
+
+    To get started execute the following code at the bottom of your settings.py
+    or in some general application file such as urls.py or a central module. The
+    only thing that matters is that it's executed right *after* the settings
+    were set up and *before* `django.contrib.jinja` is imported::
+
+        from jinja.contrib import djangosupport
+        djangosupport.configure()
+
+    What this does is setting up a Jinja environment for this django instance
+    with loaders for `TEMPLATE_DIRS` etc.  It also converts a couple of default
+    django filters such as `date` and `timesince` which are not available in
+    Jinja per default.  If you want to change the list you can provide others
+    by passing a list with filter import names as `convert_filters` keyword
+    argument.
+
+    All other keyword arguments are forwarded to the environment.  If you want
+    to provide a loader yourself pass it a loader keyword argument.
+
+    Rendering Templates
+    ===================
+
+    To render a template you can use the functions `render_to_string` or
+    `render_to_response` from the `django.contrib.jinja` module::
+
+        from django.contrib.jinja import render_to_response
+        resp = render_to_response('Hello {{ username }}!', {
+            'username':     req.session['username']
+        }, req)
+
+    `render_to_string` and `render_to_response` take at least the name of
+    the template as argument, then the optional dict which will become the
+    context.  If you also provide a request object as third argument the
+    context processors will be applied.
+
+    `render_to_response` also takes a forth parameter which can be the
+    content type which defaults to `DEFAULT_CONTENT_TYPE`.
+
+    Converting Filters
+    ==================
+
+    One of the useful objects provided by `django.contrib.jinja` is the
+    `register` object which can be used to register filters, tests and
+    global objects.  You can also convert any filter django provides in
+    a Jinja filter using `convert_django_filter`::
+
+        from django.contrib.jinja import register, convert_django_filter
+        from django.template.defaultfilters import floatformat
+
+        register.filter(convert_django_filter(floatformat), 'floatformat')
+
+    Available methods on the `register` object:
+
+    ``object (obj[, name])``
+        Register a new global as name or with the object's name.
+        Returns the function object unchanged so that you can use
+        it as decorator if no name is provided.
+
+    ``filter (func[, name])``
+        Register a function as filter with the name provided or
+        the object's name as filtername.
+        Returns the function object unchanged so that you can use
+        it as decorator if no name is provided.
+
+    ``test (func[, name])``
+        Register a function as test with the name provided or the
+        object's name as testname.
+        Returns the function object unchanged so that you can use
+        it as decorator if no name is provided.
+
+    ``context_inclusion (func, template[, name])``
+        Register a function with a name provided or the func object's
+        name in the global namespace that acts as subrender function.
+
+        func is called with the callers context as dict and the
+        arguments and keywords argument of the inclusion function.
+        The function should then process the context and return a
+        new context or the same context object. Afterwards the
+        template is rendered with this context.
+
+        Example::
+
+            def add_author(context, author=None):
+                if author is not None:
+                    author = Author.objects.get(name=author)
+                context['author'] = author
+                return context
+
+            register.context_inclusion(add_author, 'author_details.html',
+                                       'render_author_details')
+
+        You can use it in the template like this then::
+
+            {{ render_author_details('John Doe') }}
+
+    ``clean_inclusion (func, template[, name[, run_processors]]) ``
+        Works like `context_inclusion` but doesn't use the calles
+        context but an empty context. If `run_processors` is `True`
+        it will lookup the context for a `request` object and pass
+        it to the render function to apply context processors.
+
+    :copyright: 2007 by Armin Ronacher, Bryan McLemore.
+    :license: BSD, see LICENSE for more details.
+"""
+try:
+    __import__('django')
+except ImportError:
+    raise ImportError('installed django required for djangosupport')
+else:
+    from jinja.contrib._djangosupport import configure
+
+__all__ = ['configure']
diff --git a/jinja2/datastructure.py b/jinja2/datastructure.py
new file mode 100644
index 0000000..b20acd1
--- /dev/null
+++ b/jinja2/datastructure.py
@@ -0,0 +1,137 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.datastructure
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Module that helds several data types used in the template engine.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from operator import itemgetter
+from jinja.exceptions import TemplateSyntaxError, TemplateRuntimeError
+
+
+_missing = object()
+
+
+class Token(tuple):
+    """
+    Token class.
+    """
+    __slots__ = ()
+    lineno, type, value = map(itemgetter, range(3))
+
+    def __new__(cls, lineno, type, value):
+        return tuple.__new__(cls, (lineno, type, value))
+
+    def __str__(self):
+        from jinja.lexer import keywords, reverse_operators
+        if self.type in keywords:
+            return self.type
+        elif self.type in reverse_operators:
+            return reverse_operators[self.type]
+        return self.value
+
+    def __repr__(self):
+        return 'Token(%r, %r, %r)' % (
+            self.lineno,
+            self.type,
+            self.value
+        )
+
+
+class TokenStreamIterator(object):
+    """
+    The iterator for tokenstreams.  Iterate over the stream
+    until the eof token is reached.
+    """
+
+    def __init__(self, stream):
+        self._stream = stream
+
+    def __iter__(self):
+        return self
+
+    def next(self):
+        token = self._stream.current
+        if token.type == 'eof':
+            self._stream.close()
+            raise StopIteration()
+        self._stream.next()
+        return token
+
+
+class TokenStream(object):
+    """
+    A token stream wraps a generator and supports pushing tokens back.
+    It also provides some functions to expect tokens and similar stuff.
+
+    Important note: Do never push more than one token back to the
+                    stream.  Although the stream object won't stop you
+                    from doing so, the behavior is undefined. Multiple
+                    pushed tokens are only used internally!
+    """
+
+    def __init__(self, generator, filename):
+        self._next = generator.next
+        self._pushed = []
+        self.current = Token(1, 'initial', '')
+        self.filename = filename
+        self.next()
+
+    def __iter__(self):
+        return TokenStreamIterator(self)
+
+    def lineno(self):
+        """The current line number."""
+        return self.current.lineno
+    lineno = property(lineno, doc=lineno.__doc__)
+
+    def __nonzero__(self):
+        """Are we at the end of the tokenstream?"""
+        return bool(self._pushed) or self.current.type != 'eof'
+
+    eos = property(lambda x: not x.__nonzero__(), doc=__nonzero__.__doc__)
+
+    def push(self, token):
+        """Push a token back to the stream."""
+        self._pushed.append(token)
+
+    def skip(self, n):
+        """Got n tokens ahead."""
+        for x in xrange(n):
+            self.next()
+
+    def next(self):
+        """Go one token ahead."""
+        if self._pushed:
+            self.current = self._pushed.pop()
+        elif self.current.type != 'eof':
+            try:
+                self.current = self._next()
+            except StopIteration:
+                self.close()
+
+    def close(self):
+        """Close the stream."""
+        self.current = Token(self.current.lineno, 'eof', '')
+        self._next = None
+
+    def expect(self, token_type, token_value=_missing):
+        """Expect a given token type and return it"""
+        if self.current.type != token_type:
+            raise TemplateSyntaxError("expected token %r, got %r" %
+                                      (token_type, self.current.type),
+                                      self.current.lineno,
+                                      self.filename)
+        elif token_value is not _missing and \
+             self.current.value != token_value:
+            raise TemplateSyntaxError("expected %r, got %r" %
+                                      (token_value, self.current.value),
+                                      self.current.lineno,
+                                      self.filename)
+        try:
+            return self.current
+        finally:
+            self.next()
diff --git a/jinja2/defaults.py b/jinja2/defaults.py
new file mode 100644
index 0000000..37473ea
--- /dev/null
+++ b/jinja2/defaults.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.defaults
+    ~~~~~~~~~~~~~~
+
+    Jinja default filters and tags.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja.filters import FILTERS as DEFAULT_FILTERS
+from jinja.tests import TESTS as DEFAULT_TESTS
+DEFAULT_NAMESPACE = {}
+
+
+__all__ = ['DEFAULT_FILTERS', 'DEFAULT_TESTS', 'DEFAULT_NAMESPACE']
diff --git a/jinja2/environment.py b/jinja2/environment.py
new file mode 100644
index 0000000..a387ffa
--- /dev/null
+++ b/jinja2/environment.py
@@ -0,0 +1,115 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.environment
+    ~~~~~~~~~~~~~~~~~
+
+    Provides a class that holds runtime and parsing time options.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja.lexer import Lexer
+from jinja.parser import Parser
+from jinja.loaders import LoaderWrapper
+from jinja.datastructure import SilentUndefined, Markup, Context, FakeTranslator
+from jinja.utils import collect_translations, get_attribute
+from jinja.exceptions import FilterNotFound, TestNotFound, \
+     SecurityException, TemplateSyntaxError
+from jinja.defaults import DEFAULT_FILTERS, DEFAULT_TESTS, DEFAULT_NAMESPACE
+
+
+__all__ = ['Environment']
+
+
+#: minor speedup
+_getattr = getattr
+
+
+class Environment(object):
+    """
+    The Jinja environment.
+
+    The core component of Jinja is the `Environment`. It contains
+    important shared variables like configuration, filters, tests,
+    globals and others.
+    """
+
+    def __init__(self,
+                 block_start_string='{%',
+                 block_end_string='%}',
+                 variable_start_string='{{',
+                 variable_end_string='}}',
+                 comment_start_string='{#',
+                 comment_end_string='#}',
+                 trim_blocks=False,
+                 loader=None):
+        """
+        Here the possible initialization parameters:
+
+        ========================= ============================================
+        `block_start_string`      the string marking the begin of a block.
+                                  this defaults to ``'{%'``.
+        `block_end_string`        the string marking the end of a block.
+                                  defaults to ``'%}'``.
+        `variable_start_string`   the string marking the begin of a print
+                                  statement. defaults to ``'{{'``.
+        `comment_start_string`    the string marking the begin of a
+                                  comment. defaults to ``'{#'``.
+        `comment_end_string`      the string marking the end of a comment.
+                                  defaults to ``'#}'``.
+        `trim_blocks`             If this is set to ``True`` the first newline
+                                  after a block is removed (block, not
+                                  variable tag!). Defaults to ``False``.
+        `loader`                  The loader for this environment.
+        ========================= ============================================
+        """
+
+        # lexer / parser information
+        self.block_start_string = block_start_string
+        self.block_end_string = block_end_string
+        self.variable_start_string = variable_start_string
+        self.variable_end_string = variable_end_string
+        self.comment_start_string = comment_start_string
+        self.comment_end_string = comment_end_string
+        self.trim_blocks = trim_blocks
+
+        # other stuff
+        self.template_charset = template_charset
+        self.loader = loader
+
+        # defaults
+        self.filters = DEFAULT_FILTERS.copy()
+        self.tests = DEFAULT_TESTS.copy()
+        self.globals = DEFAULT_NAMESPACE.copy()
+
+        # create lexer
+        self.lexer = Lexer(self)
+
+    def loader(self, value):
+        """
+        Get or set the template loader.
+        """
+        self._loader = LoaderWrapper(self, value)
+    loader = property(lambda s: s._loader, loader, doc=loader.__doc__)
+
+    def parse(self, source, filename=None):
+        """
+        Parse the sourcecode and return the abstract syntax tree. This tree
+        of nodes is used by the `translators`_ to convert the template into
+        executable source- or bytecode.
+
+        .. _translators: translators.txt
+        """
+        parser = Parser(self, source, filename)
+        return parser.parse()
+
+    def lex(self, source, filename=None):
+        """
+        Lex the given sourcecode and return a generator that yields tokens.
+        The stream returned is not usable for Jinja but can be used if
+        Jinja templates should be processed by other tools (for example
+        syntax highlighting etc)
+
+        The tuples are returned in the form ``(lineno, token, value)``.
+        """
+        return self.lexer.tokeniter(source, filename)
diff --git a/jinja2/exceptions.py b/jinja2/exceptions.py
new file mode 100644
index 0000000..9ec5c27
--- /dev/null
+++ b/jinja2/exceptions.py
@@ -0,0 +1,42 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.exceptions
+    ~~~~~~~~~~~~~~~~
+
+    Jinja exceptions.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+
+
+class TemplateError(RuntimeError):
+    pass
+
+
+class TemplateNotFound(IOError, LookupError, TemplateError):
+    """
+    Raised if a template does not exist.
+    """
+
+    def __init__(self, name):
+        IOError.__init__(self, name)
+        self.name = name
+
+
+class TemplateSyntaxError(SyntaxError, TemplateError):
+    """
+    Raised to tell the user that there is a problem with the template.
+    """
+
+    def __init__(self, message, lineno, filename):
+        SyntaxError.__init__(self, message)
+        self.lineno = lineno
+        self.filename = filename
+
+
+class TemplateRuntimeError(TemplateError):
+    """
+    Raised by the template engine if a tag encountered an error when
+    rendering.
+    """
diff --git a/jinja2/filters.py b/jinja2/filters.py
new file mode 100644
index 0000000..6098b6e
--- /dev/null
+++ b/jinja2/filters.py
@@ -0,0 +1,985 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.filters
+    ~~~~~~~~~~~~~
+
+    Bundled jinja filters.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+from random import choice
+try:
+    from operator import itemgetter
+except ImportError:
+    itemgetter = lambda a: lambda b: b[a]
+from urllib import urlencode, quote
+
+
+_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
+
+
+def stringfilter(f):
+    """
+    Decorator for filters that just work on unicode objects.
+    """
+    def decorator(*args):
+        def wrapped(env, context, value):
+            nargs = list(args)
+            for idx, var in enumerate(nargs):
+                if isinstance(var, str):
+                    nargs[idx] = env.to_unicode(var)
+            return f(env.to_unicode(value), *nargs)
+        return wrapped
+    try:
+        decorator.__doc__ = f.__doc__
+        decorator.__name__ = f.__name__
+    except:
+        pass
+    return decorator
+
+
+def simplefilter(f):
+    """
+    Decorator for simplifying filters. Filter arguments are passed
+    to the decorated function without environment and context. The
+    source value is the first argument. (like stringfilter but
+    without unicode conversion)
+    """
+    def decorator(*args):
+        def wrapped(env, context, value):
+            return f(value, *args)
+        return wrapped
+    try:
+        decorator.__doc__ = f.__doc__
+        decorator.__name__ = f.__name__
+    except:
+        pass
+    return decorator
+
+
+def do_replace(s, old, new, count=None):
+    """
+    Return a copy of the value with all occurrences of a substring
+    replaced with a new one. The first argument is the substring
+    that should be replaced, the second is the replacement string.
+    If the optional third argument ``count`` is given, only the first
+    ``count`` occurrences are replaced:
+
+    .. sourcecode:: jinja
+
+        {{ "Hello World"|replace("Hello", "Goodbye") }}
+            -> Goodbye World
+
+        {{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
+            -> d'oh, d'oh, aaargh
+    """
+    if not isinstance(old, basestring) or \
+       not isinstance(new, basestring):
+        raise FilterArgumentError('the replace filter requires '
+                                  'string replacement arguments')
+    if count is None:
+        return s.replace(old, new)
+    if not isinstance(count, (int, long)):
+        raise FilterArgumentError('the count parameter of the '
+                                   'replace filter requires '
+                                   'an integer')
+    return s.replace(old, new, count)
+do_replace = stringfilter(do_replace)
+
+
+def do_upper(s):
+    """
+    Convert a value to uppercase.
+    """
+    return s.upper()
+do_upper = stringfilter(do_upper)
+
+
+def do_lower(s):
+    """
+    Convert a value to lowercase.
+    """
+    return s.lower()
+do_lower = stringfilter(do_lower)
+
+
+def do_escape(attribute=False):
+    """
+    XML escape ``&``, ``<``, and ``>`` in a string of data. If the
+    optional parameter is `true` this filter will also convert
+    ``"`` to ``&quot;``. This filter is just used if the environment
+    was configured with disabled `auto_escape`.
+
+    This method will have no effect it the value is already escaped.
+    """
+    #: because filters are cached we can make a local alias to
+    #: speed things up a bit
+    e = escape
+    def wrapped(env, context, s):
+        if isinstance(s, TemplateData):
+            return s
+        elif hasattr(s, '__html__'):
+            return s.__html__()
+        #: small speedup, do not convert to unicode if we already
+        #: have an unicode object.
+        if s.__class__ is not unicode:
+            s = env.to_unicode(s)
+        return e(s, attribute)
+    return wrapped
+
+
+def do_xmlattr(autospace=False):
+    """
+    Create an SGML/XML attribute string based on the items in a dict.
+    All values that are neither `none` nor `undefined` are automatically
+    escaped:
+
+    .. sourcecode:: html+jinja
+
+        <ul{{ {'class': 'my_list', 'missing': None,
+                'id': 'list-%d'|format(variable)}|xmlattr }}>
+        ...
+        </ul>
+
+    Results in something like this:
+
+    .. sourcecode:: html
+
+        <ul class="my_list" id="list-42">
+        ...
+        </ul>
+
+    As you can see it automatically prepends a space in front of the item
+    if the filter returned something. You can disable this by passing
+    `false` as only argument to the filter.
+
+    *New in Jinja 1.1*
+    """
+    e = escape
+    def wrapped(env, context, d):
+        if not hasattr(d, 'iteritems'):
+            raise TypeError('a dict is required')
+        result = []
+        for key, value in d.iteritems():
+            if value not in (None, env.undefined_singleton):
+                result.append(u'%s="%s"' % (
+                    e(env.to_unicode(key)),
+                    e(env.to_unicode(value), True)
+                ))
+        rv = u' '.join(result)
+        if autospace:
+            rv = ' ' + rv
+        return rv
+    return wrapped
+
+
+def do_capitalize(s):
+    """
+    Capitalize a value. The first character will be uppercase, all others
+    lowercase.
+    """
+    return s.capitalize()
+do_capitalize = stringfilter(do_capitalize)
+
+
+def do_title(s):
+    """
+    Return a titlecased version of the value. I.e. words will start with
+    uppercase letters, all remaining characters are lowercase.
+    """
+    return s.title()
+do_title = stringfilter(do_title)
+
+
+def do_dictsort(case_sensitive=False, by='key'):
+    """
+    Sort a dict and yield (key, value) pairs. Because python dicts are
+    unsorted you may want to use this function to order them by either
+    key or value:
+
+    .. sourcecode:: jinja
+
+        {% for item in mydict|dictsort %}
+            sort the dict by key, case insensitive
+
+        {% for item in mydict|dicsort(true) %}
+            sort the dict by key, case sensitive
+
+        {% for item in mydict|dictsort(false, 'value') %}
+            sort the dict by key, case insensitive, sorted
+            normally and ordered by value.
+    """
+    if by == 'key':
+        pos = 0
+    elif by == 'value':
+        pos = 1
+    else:
+        raise FilterArgumentError('You can only sort by either '
+                                  '"key" or "value"')
+    def sort_func(value, env):
+        if isinstance(value, basestring):
+            value = env.to_unicode(value)
+            if not case_sensitive:
+                value = value.lower()
+        return value
+
+    def wrapped(env, context, value):
+        items = value.items()
+        items.sort(lambda a, b: cmp(sort_func(a[pos], env),
+                                    sort_func(b[pos], env)))
+        return items
+    return wrapped
+
+
+def do_default(default_value=u'', boolean=False):
+    """
+    If the value is undefined it will return the passed default value,
+    otherwise the value of the variable:
+
+    .. sourcecode:: jinja
+
+        {{ my_variable|default('my_variable is not defined') }}
+
+    This will output the value of ``my_variable`` if the variable was
+    defined, otherwise ``'my_variable is not defined'``. If you want
+    to use default with variables that evaluate to false you have to
+    set the second parameter to `true`:
+
+    .. sourcecode:: jinja
+
+        {{ ''|default('the string was empty', true) }}
+    """
+    def wrapped(env, context, value):
+        if (boolean and not value) or value in (env.undefined_singleton, None):
+            return default_value
+        return value
+    return wrapped
+
+
+def do_join(d=u''):
+    """
+    Return a string which is the concatenation of the strings in the
+    sequence. The separator between elements is an empty string per
+    default, you can define ith with the optional parameter:
+
+    .. sourcecode:: jinja
+
+        {{ [1, 2, 3]|join('|') }}
+            -> 1|2|3
+
+        {{ [1, 2, 3]|join }}
+            -> 123
+    """
+    def wrapped(env, context, value):
+        return env.to_unicode(d).join([env.to_unicode(x) for x in value])
+    return wrapped
+
+
+def do_count():
+    """
+    Return the length of the value. In case if getting an integer or float
+    it will convert it into a string an return the length of the new
+    string. If the object has no length it will of corse return 0.
+    """
+    def wrapped(env, context, value):
+        try:
+            if type(value) in (int, float, long):
+                return len(str(value))
+            return len(value)
+        except TypeError:
+            return 0
+    return wrapped
+
+
+def do_reverse():
+    """
+    Return a reversed list of the sequence filtered. You can use this
+    for example for reverse iteration:
+
+    .. sourcecode:: jinja
+
+        {% for item in seq|reverse %}
+            {{ item|e }}
+        {% endfor %}
+    """
+    def wrapped(env, context, value):
+        try:
+            return value[::-1]
+        except:
+            l = list(value)
+            l.reverse()
+            return l
+    return wrapped
+
+
+def do_center(value, width=80):
+    """
+    Centers the value in a field of a given width.
+    """
+    return value.center(width)
+do_center = stringfilter(do_center)
+
+
+def do_first():
+    """
+    Return the frist item of a sequence.
+    """
+    def wrapped(env, context, seq):
+        try:
+            return iter(seq).next()
+        except StopIteration:
+            return env.undefined_singleton
+    return wrapped
+
+
+def do_last():
+    """
+    Return the last item of a sequence.
+    """
+    def wrapped(env, context, seq):
+        try:
+            return iter(reversed(seq)).next()
+        except StopIteration:
+            return env.undefined_singleton
+    return wrapped
+
+
+def do_random():
+    """
+    Return a random item from the sequence.
+    """
+    def wrapped(env, context, seq):
+        try:
+            return choice(seq)
+        except IndexError:
+            return env.undefined_singleton
+    return wrapped
+
+
+def do_urlencode():
+    """
+    urlencode a string or directory.
+
+    .. sourcecode:: jinja
+
+        {{ {'foo': 'bar', 'blub': 'blah'}|urlencode }}
+            -> foo=bar&blub=blah
+
+        {{ 'Hello World' }}
+            -> Hello%20World
+    """
+    def wrapped(env, context, value):
+        if isinstance(value, dict):
+            tmp = {}
+            for key, value in value.iteritems():
+                key = env.to_unicode(key).encode(env.charset)
+                value = env.to_unicode(value).encode(env.charset)
+                tmp[key] = value
+            return urlencode(tmp)
+        else:
+            return quote(env.to_unicode(value).encode(env.charset))
+    return wrapped
+
+
+def do_jsonencode():
+    """
+    JSON dump a variable. just works if simplejson is installed.
+
+    .. sourcecode:: jinja
+
+        {{ 'Hello World'|jsonencode }}
+            -> "Hello World"
+    """
+    global simplejson
+    try:
+        simplejson
+    except NameError:
+        import simplejson
+    return lambda e, c, v: simplejson.dumps(v)
+
+
+def do_filesizeformat():
+    """
+    Format the value like a 'human-readable' file size (i.e. 13 KB, 4.1 MB, 102
+    bytes, etc).
+    """
+    def wrapped(env, context, value):
+        # fail silently
+        try:
+            bytes = float(value)
+        except TypeError:
+            bytes = 0
+
+        if bytes < 1024:
+            return "%d Byte%s" % (bytes, bytes != 1 and 's' or '')
+        elif bytes < 1024 * 1024:
+            return "%.1f KB" % (bytes / 1024)
+        elif bytes < 1024 * 1024 * 1024:
+            return "%.1f MB" % (bytes / (1024 * 1024))
+        return "%.1f GB" % (bytes / (1024 * 1024 * 1024))
+    return wrapped
+
+
+def do_pprint(verbose=False):
+    """
+    Pretty print a variable. Useful for debugging.
+
+    With Jinja 1.2 onwards you can pass it a parameter.  If this parameter
+    is truthy the output will be more verbose (this requires `pretty`)
+    """
+    def wrapped(env, context, value):
+        return pformat(value, verbose=verbose)
+    return wrapped
+
+
+def do_urlize(value, trim_url_limit=None, nofollow=False):
+    """
+    Converts URLs in plain text into clickable links.
+
+    If you pass the filter an additional integer it will shorten the urls
+    to that number. Also a third argument exists that makes the urls
+    "nofollow":
+
+    .. sourcecode:: jinja
+
+        {{ mytext|urlize(40, True) }}
+            links are shortened to 40 chars and defined with rel="nofollow"
+    """
+    return urlize(value, trim_url_limit, nofollow)
+do_urlize = stringfilter(do_urlize)
+
+
+def do_indent(s, width=4, indentfirst=False):
+    """
+    {{ s|indent[ width[ indentfirst[ usetab]]] }}
+
+    Return a copy of the passed string, each line indented by
+    4 spaces. The first line is not indented. If you want to
+    change the number of spaces or indent the first line too
+    you can pass additional parameters to the filter:
+
+    .. sourcecode:: jinja
+
+        {{ mytext|indent(2, True) }}
+            indent by two spaces and indent the first line too.
+    """
+    indention = ' ' * width
+    if indentfirst:
+        return u'\n'.join([indention + line for line in s.splitlines()])
+    return s.replace('\n', '\n' + indention)
+do_indent = stringfilter(do_indent)
+
+
+def do_truncate(s, length=255, killwords=False, end='...'):
+    """
+    Return a truncated copy of the string. The length is specified
+    with the first parameter which defaults to ``255``. If the second
+    parameter is ``true`` the filter will cut the text at length. Otherwise
+    it will try to save the last word. If the text was in fact
+    truncated it will append an ellipsis sign (``"..."``). If you want a
+    different ellipsis sign than ``"..."`` you can specify it using the
+    third parameter.
+
+    .. sourcecode jinja::
+
+        {{ mytext|truncate(300, false, '&raquo;') }}
+            truncate mytext to 300 chars, don't split up words, use a
+            right pointing double arrow as ellipsis sign.
+    """
+    if len(s) <= length:
+        return s
+    elif killwords:
+        return s[:length] + end
+    words = s.split(' ')
+    result = []
+    m = 0
+    for word in words:
+        m += len(word) + 1
+        if m > length:
+            break
+        result.append(word)
+    result.append(end)
+    return u' '.join(result)
+do_truncate = stringfilter(do_truncate)
+
+
+def do_wordwrap(s, pos=79, hard=False):
+    """
+    Return a copy of the string passed to the filter wrapped after
+    ``79`` characters. You can override this default using the first
+    parameter. If you set the second parameter to `true` Jinja will
+    also split words apart (usually a bad idea because it makes
+    reading hard).
+    """
+    if len(s) < pos:
+        return s
+    if hard:
+        return u'\n'.join([s[idx:idx + pos] for idx in
+                          xrange(0, len(s), pos)])
+    # code from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/148061
+    return reduce(lambda line, word, pos=pos: u'%s%s%s' %
+                  (line, u' \n'[(len(line)-line.rfind('\n') - 1 +
+                                len(word.split('\n', 1)[0]) >= pos)],
+                   word), s.split(' '))
+do_wordwrap = stringfilter(do_wordwrap)
+
+
+def do_wordcount(s):
+    """
+    Count the words in that string.
+    """
+    return len([x for x in s.split() if x])
+do_wordcount = stringfilter(do_wordcount)
+
+
+def do_textile(s):
+    """
+    Prase the string using textile.
+
+    requires the `PyTextile`_ library.
+
+    .. _PyTextile: http://dealmeida.net/projects/textile/
+    """
+    from textile import textile
+    return textile(s.encode('utf-8')).decode('utf-8')
+do_textile = stringfilter(do_textile)
+
+
+def do_markdown(s):
+    """
+    Parse the string using markdown.
+
+    requires the `Python-markdown`_ library.
+
+    .. _Python-markdown: http://www.freewisdom.org/projects/python-markdown/
+    """
+    from markdown import markdown
+    return markdown(s.encode('utf-8')).decode('utf-8')
+do_markdown = stringfilter(do_markdown)
+
+
+def do_rst(s):
+    """
+    Parse the string using the reStructuredText parser from the
+    docutils package.
+
+    requires `docutils`_.
+
+    .. _docutils: http://docutils.sourceforge.net/
+    """
+    from docutils.core import publish_parts
+    parts = publish_parts(source=s, writer_name='html4css1')
+    return parts['fragment']
+do_rst = stringfilter(do_rst)
+
+
+def do_int(default=0):
+    """
+    Convert the value into an integer. If the
+    conversion doesn't work it will return ``0``. You can
+    override this default using the first parameter.
+    """
+    def wrapped(env, context, value):
+        try:
+            return int(value)
+        except (TypeError, ValueError):
+            try:
+                return int(float(value))
+            except (TypeError, ValueError):
+                return default
+    return wrapped
+
+
+def do_float(default=0.0):
+    """
+    Convert the value into a floating point number. If the
+    conversion doesn't work it will return ``0.0``. You can
+    override this default using the first parameter.
+    """
+    def wrapped(env, context, value):
+        try:
+            return float(value)
+        except (TypeError, ValueError):
+            return default
+    return wrapped
+
+
+def do_string():
+    """
+    Convert the value into an string.
+    """
+    return lambda e, c, v: e.to_unicode(v)
+
+
+def do_format(*args):
+    """
+    Apply python string formatting on an object:
+
+    .. sourcecode:: jinja
+
+        {{ "%s - %s"|format("Hello?", "Foo!") }}
+            -> Hello? - Foo!
+
+    Note that you cannot use the mapping syntax (``%(name)s``)
+    like in python. Use `|dformat` for that.
+    """
+    def wrapped(env, context, value):
+        return env.to_unicode(value) % args
+    return wrapped
+
+
+def do_dformat(d):
+    """
+    Apply python mapping string formatting on an object:
+
+    .. sourcecode:: jinja
+
+        {{ "Hello %(username)s!"|dformat({'username': 'John Doe'}) }}
+            -> Hello John Doe!
+
+    This is useful when adding variables to translateable
+    string expressions.
+
+    *New in Jinja 1.1*
+    """
+    if not isinstance(d, dict):
+        raise FilterArgumentError('dict required')
+    def wrapped(env, context, value):
+        return env.to_unicode(value) % d
+    return wrapped
+
+
+def do_trim(value):
+    """
+    Strip leading and trailing whitespace.
+    """
+    return value.strip()
+do_trim = stringfilter(do_trim)
+
+
+def do_capture(name='captured', clean=False):
+    """
+    Store the value in a variable called ``captured`` or a variable
+    with the name provided. Useful for filter blocks:
+
+    .. sourcecode:: jinja
+
+        {% filter capture('foo') %}
+            ...
+        {% endfilter %}
+        {{ foo }}
+
+    This will output "..." two times. One time from the filter block
+    and one time from the variable. If you don't want the filter to
+    output something you can use it in `clean` mode:
+
+    .. sourcecode:: jinja
+
+        {% filter capture('foo', True) %}
+            ...
+        {% endfilter %}
+        {{ foo }}
+    """
+    if not isinstance(name, basestring):
+        raise FilterArgumentError('You can only capture into variables')
+    def wrapped(env, context, value):
+        context[name] = value
+        if clean:
+            return TemplateData()
+        return value
+    return wrapped
+
+
+def do_striptags(value):
+    """
+    Strip SGML/XML tags and replace adjacent whitespace by one space.
+
+    *new in Jinja 1.1*
+    """
+    return ' '.join(_striptags_re.sub('', value).split())
+do_striptags = stringfilter(do_striptags)
+
+
+def do_slice(slices, fill_with=None):
+    """
+    Slice an iterator and return a list of lists containing
+    those items. Useful if you want to create a div containing
+    three div tags that represent columns:
+
+    .. sourcecode:: html+jinja
+
+        <div class="columwrapper">
+          {%- for column in items|slice(3) %}
+            <ul class="column-{{ loop.index }}">
+            {%- for item in column %}
+              <li>{{ item }}</li>
+            {%- endfor %}
+            </ul>
+          {%- endfor %}
+        </div>
+
+    If you pass it a second argument it's used to fill missing
+    values on the last iteration.
+
+    *new in Jinja 1.1*
+    """
+    def wrapped(env, context, value):
+        result = []
+        seq = list(value)
+        length = len(seq)
+        items_per_slice = length // slices
+        slices_with_extra = length % slices
+        offset = 0
+        for slice_number in xrange(slices):
+            start = offset + slice_number * items_per_slice
+            if slice_number < slices_with_extra:
+                offset += 1
+            end = offset + (slice_number + 1) * items_per_slice
+            tmp = seq[start:end]
+            if fill_with is not None and slice_number >= slices_with_extra:
+                tmp.append(fill_with)
+            result.append(tmp)
+        return result
+    return wrapped
+
+
+def do_batch(linecount, fill_with=None):
+    """
+    A filter that batches items. It works pretty much like `slice`
+    just the other way round. It returns a list of lists with the
+    given number of items. If you provide a second parameter this
+    is used to fill missing items. See this example:
+
+    .. sourcecode:: html+jinja
+
+        <table>
+        {%- for row in items|batch(3, '&nbsp;') %}
+          <tr>
+          {%- for column in row %}
+            <tr>{{ column }}</td>
+          {%- endfor %}
+          </tr>
+        {%- endfor %}
+        </table>
+
+    *new in Jinja 1.1*
+    """
+    def wrapped(env, context, value):
+        result = []
+        tmp = []
+        for item in value:
+            if len(tmp) == linecount:
+                result.append(tmp)
+                tmp = []
+            tmp.append(item)
+        if tmp:
+            if fill_with is not None and len(tmp) < linecount:
+                tmp += [fill_with] * (linecount - len(tmp))
+            result.append(tmp)
+        return result
+    return wrapped
+
+
+def do_sum():
+    """
+    Sum up the given sequence of numbers.
+
+    *new in Jinja 1.1*
+    """
+    def wrapped(env, context, value):
+        return sum(value)
+    return wrapped
+
+
+def do_abs():
+    """
+    Return the absolute value of a number.
+
+    *new in Jinja 1.1*
+    """
+    def wrapped(env, context, value):
+        return abs(value)
+    return wrapped
+
+
+def do_round(precision=0, method='common'):
+    """
+    Round the number to a given precision. The first
+    parameter specifies the precision (default is ``0``), the
+    second the rounding method:
+
+    - ``'common'`` rounds either up or down
+    - ``'ceil'`` always rounds up
+    - ``'floor'`` always rounds down
+
+    If you don't specify a method ``'common'`` is used.
+
+    .. sourcecode:: jinja
+
+        {{ 42.55|round }}
+            -> 43
+        {{ 42.55|round(1, 'floor') }}
+            -> 42.5
+
+    *new in Jinja 1.1*
+    """
+    if not method in ('common', 'ceil', 'floor'):
+        raise FilterArgumentError('method must be common, ceil or floor')
+    if precision < 0:
+        raise FilterArgumentError('precision must be a postive integer '
+                                  'or zero.')
+    def wrapped(env, context, value):
+        if method == 'common':
+            return round(value, precision)
+        import math
+        func = getattr(math, method)
+        if precision:
+            return func(value * 10 * precision) / (10 * precision)
+        else:
+            return func(value)
+    return wrapped
+
+
+def do_sort(reverse=False):
+    """
+    Sort a sequence. Per default it sorts ascending, if you pass it
+    `True` as first argument it will reverse the sorting.
+
+    *new in Jinja 1.1*
+    """
+    def wrapped(env, context, value):
+        return sorted(value, reverse=reverse)
+    return wrapped
+
+
+def do_groupby(attribute):
+    """
+    Group a sequence of objects by a common attribute.
+
+    If you for example have a list of dicts or objects that represent persons
+    with `gender`, `first_name` and `last_name` attributes and you want to
+    group all users by genders you can do something like the following
+    snippet:
+
+    .. sourcecode:: html+jinja
+
+        <ul>
+        {% for group in persons|groupby('gender') %}
+            <li>{{ group.grouper }}<ul>
+            {% for person in group.list %}
+                <li>{{ person.first_name }} {{ person.last_name }}</li>
+            {% endfor %}</ul></li>
+        {% endfor %}
+        </ul>
+
+    As you can see the item we're grouping by is stored in the `grouper`
+    attribute and the `list` contains all the objects that have this grouper
+    in common.
+
+    *New in Jinja 1.2*
+    """
+    def wrapped(env, context, value):
+        expr = lambda x: env.get_attribute(x, attribute)
+        return sorted([{
+            'grouper':  a,
+            'list':     list(b)
+        } for a, b in groupby(sorted(value, key=expr), expr)],
+            key=itemgetter('grouper'))
+    return wrapped
+
+
+def do_getattribute(attribute):
+    """
+    Get one attribute from an object. Normally you don't have to use this
+    filter because the attribute and subscript expressions try to either
+    get an attribute of an object or an item. In some situations it could
+    be that there is an item *and* an attribute with the same name. In that
+    situation only the item is returned, never the attribute.
+
+    .. sourcecode:: jinja
+
+        {{ foo.bar }} -> {{ foo|getattribute('bar') }}
+
+    *New in Jinja 1.2*
+    """
+    def wrapped(env, context, value):
+        try:
+            return get_attribute(value, attribute)
+        except (SecurityException, AttributeError):
+            return env.undefined_singleton
+    return wrapped
+
+
+def do_getitem(key):
+    """
+    This filter basically works like the normal subscript expression but
+    it doesn't fall back to attribute lookup. If an item does not exist for
+    an object undefined is returned.
+
+    .. sourcecode:: jinja
+
+        {{ foo.bar }} -> {{ foo|getitem('bar') }}
+
+    *New in Jinja 1.2*
+    """
+    def wrapped(env, context, value):
+        try:
+            return value[key]
+        except (TypeError, KeyError, IndexError, AttributeError):
+            return env.undefined_singleton
+    return wrapped
+
+
+FILTERS = {
+    'replace':              do_replace,
+    'upper':                do_upper,
+    'lower':                do_lower,
+    'escape':               do_escape,
+    'e':                    do_escape,
+    'xmlattr':              do_xmlattr,
+    'capitalize':           do_capitalize,
+    'title':                do_title,
+    'default':              do_default,
+    'join':                 do_join,
+    'count':                do_count,
+    'dictsort':             do_dictsort,
+    'length':               do_count,
+    'reverse':              do_reverse,
+    'center':               do_center,
+    'title':                do_title,
+    'capitalize':           do_capitalize,
+    'first':                do_first,
+    'last':                 do_last,
+    'random':               do_random,
+    'urlencode':            do_urlencode,
+    'jsonencode':           do_jsonencode,
+    'filesizeformat':       do_filesizeformat,
+    'pprint':               do_pprint,
+    'indent':               do_indent,
+    'truncate':             do_truncate,
+    'wordwrap':             do_wordwrap,
+    'wordcount':            do_wordcount,
+    'textile':              do_textile,
+    'markdown':             do_markdown,
+    'rst':                  do_rst,
+    'int':                  do_int,
+    'float':                do_float,
+    'string':               do_string,
+    'urlize':               do_urlize,
+    'format':               do_format,
+    'dformat':              do_dformat,
+    'capture':              do_capture,
+    'trim':                 do_trim,
+    'striptags':            do_striptags,
+    'slice':                do_slice,
+    'batch':                do_batch,
+    'sum':                  do_sum,
+    'abs':                  do_abs,
+    'round':                do_round,
+    'sort':                 do_sort,
+    'groupby':              do_groupby,
+    'getattribute':         do_getattribute,
+    'getitem':              do_getitem
+}
diff --git a/jinja2/lexer.py b/jinja2/lexer.py
new file mode 100644
index 0000000..7752bee
--- /dev/null
+++ b/jinja2/lexer.py
@@ -0,0 +1,503 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.lexer
+    ~~~~~~~~~~~~
+
+    This module implements a Jinja / Python combination lexer. The
+    `Lexer` class provided by this module is used to do some preprocessing
+    for Jinja.
+
+    On the one hand it filters out invalid operators like the bitshift
+    operators we don't allow in templates. On the other hand it separates
+    template code and python code in expressions.
+
+    :copyright: 2007-2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import unicodedata
+from jinja.datastructure import TokenStream, Token
+from jinja.exceptions import TemplateSyntaxError
+from weakref import WeakValueDictionary
+
+
+__all__ = ['Lexer', 'Failure', 'keywords']
+
+
+# cache for the lexers. Exists in order to be able to have multiple
+# environments with the same lexer
+_lexer_cache = WeakValueDictionary()
+
+
+# static regular expressions
+whitespace_re = re.compile(r'\s+(?um)')
+string_re = re.compile(r"('([^'\\]*(?:\\.[^'\\]*)*)'"
+                       r'|"([^"\\]*(?:\\.[^"\\]*)*)")(?ms)')
+integer_re = re.compile(r'\d+')
+name_re = re.compile(r'[a-zA-Z_][a-zA-Z0-9_]*')
+float_re = re.compile(r'\d+\.\d+')
+
+
+# set of used keywords
+keywords = set(['and', 'block', 'elif', 'else', 'endblock',
+                'endfilter', 'endfor', 'endif', 'endmacro', 'endraw',
+                'endtrans', 'extends', 'filter', 'for', 'if', 'in',
+                'include', 'is', 'macro', 'not', 'or', 'pluralize', 'raw',
+                'recursive', 'set', 'trans', 'call', 'endcall',
+                'true', 'false', 'none'])
+
+# bind operators to token types
+operators = {
+    '+':            'add',
+    '-':            'sub',
+    '/':            'div',
+    '//':           'floordiv',
+    '*':            'mul',
+    '%':            'mod',
+    '**':           'pow',
+    '~':            'tilde',
+    '[':            'lbracket',
+    ']':            'rbracket',
+    '(':            'lparen',
+    ')':            'rparen',
+    '{':            'lbrace',
+    '}':            'rbrace',
+    '==':           'eq',
+    '!=':           'ne',
+    '>':            'gt',
+    '>=':           'gteq',
+    '<':            'lt',
+    '<=':           'lteq',
+    '=':            'assign',
+    '.':            'dot',
+    ':':            'colon',
+    '|':            'pipe',
+    ',':            'comma',
+    ';':            'semicolon'
+}
+
+reverse_operators = dict([(v, k) for k, v in operators.iteritems()])
+assert len(operators) == len(reverse_operators), 'operators dropped'
+operator_re = re.compile('(%s)' % '|'.join([re.escape(x) for x in
+                         sorted(operators, key=lambda x: -len(x))]))
+
+simple_escapes = {
+    'a':    '\a',
+    'n':    '\n',
+    'r':    '\r',
+    'f':    '\f',
+    't':    '\t',
+    'v':    '\v',
+    '\\':   '\\',
+    '"':    '"',
+    "'":    "'",
+    '0':    '\x00'
+}
+unicode_escapes = {
+    'x':    2,
+    'u':    4,
+    'U':    8
+}
+
+
+def unescape_string(lineno, filename, s):
+    r"""
+    Unescape a string. Supported escapes:
+        \a, \n, \r\, \f, \v, \\, \", \', \0
+
+        \x00, \u0000, \U00000000, \N{...}
+
+    Not supported are \101 because imho redundant.
+    """
+    result = []
+    write = result.append
+    chariter = iter(s)
+    next_char = chariter.next
+
+    # faster lookup
+    sescapes = simple_escapes
+    uescapes = unicode_escapes
+
+    try:
+        for char in chariter:
+            if char == '\\':
+                char = next_char()
+                if char in sescapes:
+                    write(sescapes[char])
+                elif char in uescapes:
+                    seq = [next_char() for x in xrange(uescapes[char])]
+                    try:
+                        write(unichr(int(''.join(seq), 16)))
+                    except ValueError:
+                        raise TemplateSyntaxError('invalid unicode codepoint',
+                                                  lineno, filename)
+                elif char == 'N':
+                    if next_char() != '{':
+                        raise TemplateSyntaxError('no name for codepoint',
+                                                  lineno, filename)
+                    seq = []
+                    while 1:
+                        char = next_char()
+                        if char == '}':
+                            break
+                        seq.append(char)
+                    try:
+                        write(unicodedata.lookup(u''.join(seq)))
+                    except KeyError:
+                        raise TemplateSyntaxError('unknown character name',
+                                                  lineno, filename)
+                else:
+                    write('\\' + char)
+            else:
+                write(char)
+    except StopIteration:
+        raise TemplateSyntaxError('invalid string escape', lineno, filename)
+    return u''.join(result)
+
+
+def unescape_regex(s):
+    """
+    Unescape rules for regular expressions.
+    """
+    buffer = []
+    write = buffer.append
+    in_escape = False
+    for char in s:
+        if in_escape:
+            in_escape = False
+            if char not in safe_chars:
+                write('\\' + char)
+                continue
+        write(char)
+    return u''.join(buffer)
+
+
+class Failure(object):
+    """
+    Class that raises a `TemplateSyntaxError` if called.
+    Used by the `Lexer` to specify known errors.
+    """
+
+    def __init__(self, message, cls=TemplateSyntaxError):
+        self.message = message
+        self.error_class = cls
+
+    def __call__(self, lineno, filename):
+        raise self.error_class(self.message, lineno, filename)
+
+
+class LexerMeta(type):
+    """
+    Metaclass for the lexer that caches instances for
+    the same configuration in a weak value dictionary.
+    """
+
+    def __call__(cls, environment):
+        key = hash((environment.block_start_string,
+                    environment.block_end_string,
+                    environment.variable_start_string,
+                    environment.variable_end_string,
+                    environment.comment_start_string,
+                    environment.comment_end_string,
+                    environment.trim_blocks))
+
+        # use the cached lexer if possible
+        if key in _lexer_cache:
+            return _lexer_cache[key]
+
+        # create a new lexer and cache it
+        lexer = type.__call__(cls, environment)
+        _lexer_cache[key] = lexer
+        return lexer
+
+
+class Lexer(object):
+    """
+    Class that implements a lexer for a given environment. Automatically
+    created by the environment class, usually you don't have to do that.
+
+    Note that the lexer is not automatically bound to an environment.
+    Multiple environments can share the same lexer.
+    """
+
+    __metaclass__ = LexerMeta
+
+    def __init__(self, environment):
+        # shortcuts
+        c = lambda x: re.compile(x, re.M | re.S)
+        e = re.escape
+
+        # lexing rules for tags
+        tag_rules = [
+            (whitespace_re, None, None),
+            (float_re, 'float', None),
+            (integer_re, 'integer', None),
+            ('%s' % '|'.join(sorted(keywords, key=lambda x: -len(x))),
+             'keyword', None),
+            (name_re, 'name', None),
+            (string_re, 'string', None),
+            (operator_re, 'operator', None)
+        ]
+
+        #: if variables and blocks have the same delimiters we won't
+        #: receive any variable blocks in the parser. This variable is `True`
+        #: if we need that.
+        self.no_variable_block = (
+            (environment.variable_start_string is
+             environment.variable_end_string is None) or
+            (environment.variable_start_string ==
+             environment.block_start_string and
+             environment.variable_end_string ==
+             environment.block_end_string)
+        )
+
+        # assamble the root lexing rule. because "|" is ungreedy
+        # we have to sort by length so that the lexer continues working
+        # as expected when we have parsing rules like <% for block and
+        # <%= for variables. (if someone wants asp like syntax)
+        # variables are just part of the rules if variable processing
+        # is required.
+        root_tag_rules = [
+            ('comment',     environment.comment_start_string),
+            ('block',       environment.block_start_string)
+        ]
+        if not self.no_variable_block:
+            root_tag_rules.append(('variable',
+                                   environment.variable_start_string))
+        root_tag_rules.sort(lambda a, b: cmp(len(b[1]), len(a[1])))
+
+        # block suffix if trimming is enabled
+        block_suffix_re = environment.trim_blocks and '\\n?' or ''
+
+        # global lexing rules
+        self.rules = {
+            'root': [
+                # directives
+                (c('(.*?)(?:%s)' % '|'.join(
+                    ['(?P<raw_begin>(?:\s*%s\-|%s)\s*raw\s*%s)' % (
+                        e(environment.block_start_string),
+                        e(environment.block_start_string),
+                        e(environment.block_end_string)
+                    )] + [
+                        '(?P<%s_begin>\s*%s\-|%s)' % (n, e(r), e(r))
+                        for n, r in root_tag_rules
+                    ])), ('data', '#bygroup'), '#bygroup'),
+                # data
+                (c('.+'), 'data', None)
+            ],
+            # comments
+            'comment_begin': [
+                (c(r'(.*?)((?:\-%s\s*|%s)%s)' % (
+                    e(environment.comment_end_string),
+                    e(environment.comment_end_string),
+                    block_suffix_re
+                )), ('comment', 'comment_end'), '#pop'),
+                (c('(.)'), (Failure('Missing end of comment tag'),), None)
+            ],
+            # blocks
+            'block_begin': [
+                (c('(?:\-%s\s*|%s)%s' % (
+                    e(environment.block_end_string),
+                    e(environment.block_end_string),
+                    block_suffix_re
+                )), 'block_end', '#pop'),
+            ] + tag_rules,
+            # raw block
+            'raw_begin': [
+                (c('(.*?)((?:\s*%s\-|%s)\s*endraw\s*(?:\-%s\s*|%s%s))' % (
+                    e(environment.block_start_string),
+                    e(environment.block_start_string),
+                    e(environment.block_end_string),
+                    e(environment.block_end_string),
+                    block_suffix_re
+                )), ('data', 'raw_end'), '#pop'),
+                (c('(.)'), (Failure('Missing end of raw directive'),), None)
+            ]
+        }
+
+        # only add the variable rules to the list if we process variables
+        # the variable_end_string variable could be None and break things.
+        if not self.no_variable_block:
+            self.rules['variable_begin'] = [
+                (c('\-%s\s*|%s' % (
+                    e(environment.variable_end_string),
+                    e(environment.variable_end_string)
+                )), 'variable_end', '#pop')
+            ] + tag_rules
+
+    def tokenize(self, source, filename=None):
+        """
+        Works like `tokeniter` but returns a tokenstream of tokens and not a
+        generator or token tuples. Additionally all token values are already
+        converted into types and postprocessed. For example keywords are
+        already keyword tokens, not named tokens, comments are removed,
+        integers and floats converted, strings unescaped etc.
+        """
+        def generate():
+            for lineno, token, value in self.tokeniter(source, filename):
+                if token in ('comment_begin', 'comment', 'comment_end'):
+                    continue
+                elif token == 'data':
+                    try:
+                        value = str(value)
+                    except UnicodeError:
+                        pass
+                elif token == 'keyword':
+                    token = str(value)
+                elif token == 'name':
+                    value = str(value)
+                elif token == 'string':
+                    value = unescape_string(lineno, filename, value[1:-1])
+                    try:
+                        value = str(value)
+                    except UnicodeError:
+                        pass
+                elif token == 'integer':
+                    value = int(value)
+                elif token == 'float':
+                    value = float(value)
+                elif token == 'operator':
+                    token = operators[value]
+                    value = ''
+                yield Token(lineno, token, value)
+        return TokenStream(generate(), filename)
+
+    def tokeniter(self, source, filename=None):
+        """
+        This method tokenizes the text and returns the tokens in a generator.
+        Use this method if you just want to tokenize a template. The output
+        you get is not compatible with the input the jinja parser wants. The
+        parser uses the `tokenize` function with returns a `TokenStream` and
+        keywords instead of just names.
+        """
+        source = '\n'.join(source.splitlines())
+        pos = 0
+        lineno = 1
+        stack = ['root']
+        statetokens = self.rules['root']
+        source_length = len(source)
+
+        balancing_stack = []
+
+        while True:
+            # tokenizer loop
+            for regex, tokens, new_state in statetokens:
+                m = regex.match(source, pos)
+                # if no match we try again with the next rule
+                if not m:
+                    continue
+
+                # we only match blocks and variables if brances / parentheses
+                # are balanced. continue parsing with the lower rule which
+                # is the operator rule. do this only if the end tags look
+                # like operators
+                if balancing_stack and \
+                   tokens in ('variable_end', 'block_end'):
+                    continue
+
+                # tuples support more options
+                if isinstance(tokens, tuple):
+                    for idx, token in enumerate(tokens):
+                        # hidden group
+                        if token is None:
+                            g = m.group(idx)
+                            if g:
+                                lineno += g.count('\n')
+                            continue
+                        # failure group
+                        elif token.__class__ is Failure:
+                            raise token(lineno, filename)
+                        # bygroup is a bit more complex, in that case we
+                        # yield for the current token the first named
+                        # group that matched
+                        elif token == '#bygroup':
+                            for key, value in m.groupdict().iteritems():
+                                if value is not None:
+                                    yield lineno, key, value
+                                    lineno += value.count('\n')
+                                    break
+                            else:
+                                raise RuntimeError('%r wanted to resolve '
+                                                   'the token dynamically'
+                                                   ' but no group matched'
+                                                   % regex)
+                        # normal group
+                        else:
+                            data = m.group(idx + 1)
+                            if data:
+                                yield lineno, token, data
+                            lineno += data.count('\n')
+
+                # strings as token just are yielded as it, but just
+                # if the data is not empty
+                else:
+                    data = m.group()
+                    # update brace/parentheses balance
+                    if tokens == 'operator':
+                        if data == '{':
+                            balancing_stack.append('}')
+                        elif data == '(':
+                            balancing_stack.append(')')
+                        elif data == '[':
+                            balancing_stack.append(']')
+                        elif data in ('}', ')', ']'):
+                            if not balancing_stack:
+                                raise TemplateSyntaxError('unexpected "%s"' %
+                                                          data, lineno,
+                                                          filename)
+                            expected_op = balancing_stack.pop()
+                            if expected_op != data:
+                                raise TemplateSyntaxError('unexpected "%s", '
+                                                          'expected "%s"' %
+                                                          (data, expected_op),
+                                                          lineno, filename)
+                    # yield items
+                    if tokens is not None:
+                        if data:
+                            yield lineno, tokens, data
+                    lineno += data.count('\n')
+
+                # fetch new position into new variable so that we can check
+                # if there is a internal parsing error which would result
+                # in an infinite loop
+                pos2 = m.end()
+
+                # handle state changes
+                if new_state is not None:
+                    # remove the uppermost state
+                    if new_state == '#pop':
+                        stack.pop()
+                    # resolve the new state by group checking
+                    elif new_state == '#bygroup':
+                        for key, value in m.groupdict().iteritems():
+                            if value is not None:
+                                stack.append(key)
+                                break
+                        else:
+                            raise RuntimeError('%r wanted to resolve the '
+                                               'new state dynamically but'
+                                               ' no group matched' %
+                                               regex)
+                    # direct state name given
+                    else:
+                        stack.append(new_state)
+                    statetokens = self.rules[stack[-1]]
+                # we are still at the same position and no stack change.
+                # this means a loop without break condition, avoid that and
+                # raise error
+                elif pos2 == pos:
+                    raise RuntimeError('%r yielded empty string without '
+                                       'stack change' % regex)
+                # publish new function and start again
+                pos = pos2
+                break
+            # if loop terminated without break we havn't found a single match
+            # either we are at the end of the file or we have a problem
+            else:
+                # end of text
+                if pos >= source_length:
+                    return
+                # something went wrong
+                raise TemplateSyntaxError('unexpected char %r at %d' %
+                                          (source[pos], pos), lineno,
+                                          filename)
diff --git a/jinja2/loaders.py b/jinja2/loaders.py
new file mode 100644
index 0000000..40bc1d7
--- /dev/null
+++ b/jinja2/loaders.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.loaders
+    ~~~~~~~~~~~~~
+
+    Jinja loader classes.
+
+    :copyright: 2007 by Armin Ronacher, Bryan McLemore.
+    :license: BSD, see LICENSE for more details.
+"""
diff --git a/jinja2/nodes.py b/jinja2/nodes.py
new file mode 100644
index 0000000..1e20096
--- /dev/null
+++ b/jinja2/nodes.py
@@ -0,0 +1,494 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.nodes
+    ~~~~~~~~~~~~
+
+    This module implements additional nodes derived from the ast base node.
+
+    It also provides some node tree helper functions like `in_lineno` and
+    `get_nodes` used by the parser and translator in order to normalize
+    python and jinja nodes.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import operator
+from itertools import chain, izip
+from copy import copy
+
+
+_binop_to_func = {
+    '*':        operator.mul,
+    '/':        operator.truediv,
+    '//':       operator.floordiv,
+    '**':       operator.pow,
+    '%':        operator.mod,
+    '+':        operator.add,
+    '-':        operator.sub
+}
+
+_uaop_to_func = {
+    'not':      operator.not_,
+    '+':        operator.pos,
+    '-':        operator.neg
+}
+
+
+class Impossible(Exception):
+    """
+    Raised if the node could not perform a requested action.
+    """
+
+
+class NodeType(type):
+
+    def __new__(cls, name, bases, d):
+        for attr in '_fields', '_attributes':
+            storage = []
+            for base in bases:
+                storage.extend(getattr(base, attr, ()))
+            storage.extend(d.get(attr, ()))
+            assert len(storage) == len(set(storage))
+            d[attr] = tuple(storage)
+        return type.__new__(cls, name, bases, d)
+
+
+class Node(object):
+    """
+    Base jinja node.
+    """
+    __metaclass__ = NodeType
+    _fields = ()
+    _attributes = ('lineno',)
+
+    def __init__(self, *args, **kw):
+        if args:
+            if len(args) != len(self._fields):
+                if not self._fields:
+                    raise TypeError('%r takes 0 arguments' %
+                                    self.__class__.__name__)
+                raise TypeError('%r takes 0 or %d argument%s' % (
+                    self.__class__.__name__,
+                    len(self._fields),
+                    len(self._fields) != 1 and 's' or ''
+                ))
+            for name, arg in izip(self._fields, args):
+                setattr(self, name, arg)
+        for attr in self._attributes:
+            setattr(self, attr, kw.pop(attr, None))
+        if kw:
+            raise TypeError('unknown keyword argument %r' %
+                            iter(kw).next())
+
+    def iter_fields(self):
+        for name in self._fields:
+            try:
+                yield name, getattr(self, name)
+            except AttributeError:
+                pass
+
+    def iter_child_nodes(self):
+        for field, item in self.iter_fields():
+            if isinstance(item, list):
+                for n in item:
+                    if isinstance(n, Node):
+                        yield n
+            elif isinstance(item, Node):
+                yield item
+
+    def __repr__(self):
+        return '%s(%s)' % (
+            self.__class__.__name__,
+            ', '.join('%s=%r' % (arg, getattr(self, arg, None)) for
+                      arg in self._fields)
+        )
+
+
+class Stmt(Node):
+    """
+    Base node for all statements.
+    """
+
+
+class Helper(Node):
+    """
+    Nodes that exist in a specific context only.
+    """
+
+
+class Template(Node):
+    """
+    Node that represents a template.
+    """
+    _fields = ('extends', 'body')
+
+
+class Output(Stmt):
+    """
+    A node that holds multiple expressions which are then printed out.  This
+    is used both for the `print` statement and the regular template data.
+    """
+    _fields = ('nodes',)
+
+
+class Extends(Stmt):
+    """
+    Represents an extends statement.
+    """
+    _fields = ('extends',)
+
+
+class For(Stmt):
+    """
+    A node that represents a for loop
+    """
+    _fields = ('item', 'seq', 'body', 'else_', 'recursive')
+
+
+class If(Stmt):
+    """
+    A node that represents an if condition.
+    """
+    _fields = ('test', 'body', 'else_')
+
+
+class Macro(Stmt):
+    """
+    A node that represents a macro.
+    """
+    _fields = ('name', 'arguments', 'body')
+
+
+class CallBlock(Stmt):
+    """
+    A node that represents am extended macro call.
+    """
+    _fields = ('expr', 'body')
+
+
+class Set(Stmt):
+    """
+    Allows defining own variables.
+    """
+    _fields = ('name', 'expr')
+
+
+class FilterBlock(Stmt):
+    """
+    Node for filter sections.
+    """
+    _fields = ('body', 'filters')
+
+
+class Block(Stmt):
+    """
+    A node that represents a block.
+    """
+    _fields = ('name', 'body')
+
+
+class Include(Stmt):
+    """
+    A node that represents the include tag.
+    """
+    _fields = ('template',)
+
+
+class Trans(Stmt):
+    """
+    A node for translatable sections.
+    """
+    _fields = ('singular', 'plural', 'indicator', 'replacements')
+
+
+class ExprStmt(Stmt):
+    """
+    A statement that evaluates an expression to None.
+    """
+    _fields = ('node',)
+
+
+class Expr(Node):
+    """
+    Baseclass for all expressions.
+    """
+
+    def as_const(self):
+        """
+        Return the value of the expression as constant or raise `Impossible`
+        if this was not possible.
+        """
+        raise Impossible()
+
+    def can_assign(self):
+        """
+        Check if it's possible to assign something to this node.
+        """
+        return False
+
+
+class BinExpr(Expr):
+    """
+    Baseclass for all binary expressions.
+    """
+    _fields = ('left', 'right')
+    operator = None
+
+    def as_const(self):
+        f = _binop_to_func[self.operator]
+        try:
+            return f(self.left.as_const(), self.right.as_const())
+        except:
+            print self.left, f, self.right
+            raise Impossible()
+
+
+class UnaryExpr(Expr):
+    """
+    Baseclass for all unary expressions.
+    """
+    _fields = ('node',)
+    operator = None
+
+    def as_const(self):
+        f = _uaop_to_func[self.operator]
+        try:
+            return f(self.node.as_const())
+        except:
+            raise Impossible()
+
+
+class Name(Expr):
+    """
+    any name such as {{ foo }}
+    """
+    _fields = ('name',)
+
+    def can_assign(self):
+        return True
+
+
+class Literal(Expr):
+    """
+    Baseclass for literals.
+    """
+
+
+class Const(Literal):
+    """
+    any constat such as {{ "foo" }}
+    """
+    _fields = ('value',)
+
+    def as_const(self):
+        return self.value
+
+
+class Tuple(Literal):
+    """
+    For loop unpacking and some other things like multiple arguments
+    for subscripts.
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return tuple(x.as_const() for x in self.items)
+
+    def can_assign(self):
+        for item in self.items:
+            if not item.can_assign():
+                return False
+        return True
+
+
+class List(Literal):
+    """
+    any list literal such as {{ [1, 2, 3] }}
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return [x.as_const() for x in self.items]
+
+
+class Dict(Literal):
+    """
+    any dict literal such as {{ {1: 2, 3: 4} }}
+    """
+    _fields = ('items',)
+
+    def as_const(self):
+        return dict(x.as_const() for x in self.items)
+
+
+class Pair(Helper):
+    """
+    A key, value pair for dicts.
+    """
+    _fields = ('key', 'value')
+
+    def as_const(self):
+        return self.key.as_const(), self.value.as_const()
+
+
+class CondExpr(Expr):
+    """
+    {{ foo if bar else baz }}
+    """
+    _fields = ('test', 'expr1', 'expr2')
+
+    def as_const(self):
+        if self.test.as_const():
+            return self.expr1.as_const()
+        return self.expr2.as_const()
+
+
+class Filter(Expr):
+    """
+    {{ foo|bar|baz }}
+    """
+    _fields = ('node', 'filters')
+
+
+class Test(Expr):
+    """
+    {{ foo is lower }}
+    """
+    _fields = ('node', 'name', 'args')
+
+
+class Call(Expr):
+    """
+    {{ foo(bar) }}
+    """
+    _fields = ('node', 'args', 'kwargs', 'dyn_args', 'dyn_kwargs')
+
+
+class Subscript(Expr):
+    """
+    {{ foo.bar }} and {{ foo['bar'] }} etc.
+    """
+    _fields = ('node', 'arg')
+
+    def as_const(self):
+        try:
+            return self.node.as_const()[self.node.as_const()]
+        except:
+            raise Impossible()
+
+    def can_assign(self):
+        return True
+
+
+class Slice(Expr):
+    """
+    1:2:3 etc.
+    """
+    _fields = ('start', 'stop', 'step')
+
+
+class Concat(Expr):
+    """
+    For {{ foo ~ bar }}.  Concatenates strings.
+    """
+    _fields = ('nodes',)
+
+    def as_const(self):
+        return ''.join(unicode(x.as_const()) for x in self.nodes)
+
+
+class Compare(Expr):
+    """
+    {{ foo == bar }}, {{ foo >= bar }} etc.
+    """
+    _fields = ('expr', 'ops')
+
+
+class Mul(BinExpr):
+    """
+    {{ foo * bar }}
+    """
+    operator = '*'
+
+
+class Div(BinExpr):
+    """
+    {{ foo / bar }}
+    """
+    operator = '/'
+
+
+class FloorDiv(BinExpr):
+    """
+    {{ foo // bar }}
+    """
+    operator = '//'
+
+
+class Add(BinExpr):
+    """
+    {{ foo + bar }}
+    """
+    operator = '+'
+
+
+class Sub(BinExpr):
+    """
+    {{ foo - bar }}
+    """
+    operator = '-'
+
+
+class Mod(BinExpr):
+    """
+    {{ foo % bar }}
+    """
+    operator = '%'
+
+
+class Pow(BinExpr):
+    """
+    {{ foo ** bar }}
+    """
+    operator = '**'
+
+
+class And(BinExpr):
+    """
+    {{ foo and bar }}
+    """
+    operator = 'and'
+
+    def as_const(self):
+        return self.left.as_const() and self.right.as_const()
+
+
+class Or(BinExpr):
+    """
+    {{ foo or bar }}
+    """
+    operator = 'or'
+
+    def as_const(self):
+        return self.left.as_const() or self.right.as_const()
+
+
+class Not(UnaryExpr):
+    """
+    {{ not foo }}
+    """
+    operator = 'not'
+
+
+class NegExpr(UnaryExpr):
+    """
+    {{ -foo }}
+    """
+    operator = '-'
+
+
+class PosExpr(UnaryExpr):
+    """
+    {{ +foo }}
+    """
+    operator = '+'
diff --git a/jinja2/parser.py b/jinja2/parser.py
new file mode 100644
index 0000000..d981c75
--- /dev/null
+++ b/jinja2/parser.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.parser
+    ~~~~~~~~~~~~~
+
+    Implements the template parser.
+
+    :copyright: 2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+from jinja import nodes
+from jinja.exceptions import TemplateSyntaxError
+
+
+__all__ = ['Parser']
+
+
+class Parser(object):
+    """
+    The template parser class.
+
+    Transforms sourcecode into an abstract syntax tree.
+    """
+
+    def __init__(self, environment, source, filename=None):
+        self.environment = environment
+        if isinstance(source, str):
+            source = source.decode(environment.template_charset, 'ignore')
+        if isinstance(filename, unicode):
+            filename = filename.encode('utf-8')
+        self.source = source
+        self.filename = filename
+        self.closed = False
+        self.blocks = set()
+        self.no_variable_block = self.environment.lexer.no_variable_block
+        self.stream = environment.lexer.tokenize(source, filename)
+
+    def parse(self):
+        pass
diff --git a/jinja2/tests.py b/jinja2/tests.py
new file mode 100644
index 0000000..25f7992
--- /dev/null
+++ b/jinja2/tests.py
@@ -0,0 +1,143 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.tests
+    ~~~~~~~~~~~
+
+    Jinja test functions. Used with the "is" operator.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+
+
+number_re = re.compile(r'^-?\d+(\.\d+)?$')
+regex_type = type(number_re)
+
+
+def test_odd():
+    """
+    Return true if the variable is odd.
+    """
+    return lambda e, c, v: v % 2 == 1
+
+
+def test_even():
+    """
+    Return true of the variable is even.
+    """
+    return lambda e, c, v: v % 2 == 0
+
+
+def test_defined():
+    """
+    Return true if the variable is defined:
+
+    .. sourcecode:: jinja
+
+        {% if variable is defined %}
+            value of variable: {{ variable }}
+        {% else %}
+            variable is not defined
+        {% endif %}
+
+    See also the ``default`` filter.
+    """
+    return lambda e, c, v: v is not e.undefined_singleton
+
+
+def test_lower():
+    """
+    Return true if the variable is lowercase.
+    """
+    return lambda e, c, v: isinstance(v, basestring) and v.islower()
+
+
+def test_upper():
+    """
+    Return true if the variable is uppercase.
+    """
+    return lambda e, c, v: isinstance(v, basestring) and v.isupper()
+
+
+def test_numeric():
+    """
+    Return true if the variable is numeric.
+    """
+    return lambda e, c, v: isinstance(v, (int, long, float)) or (
+                           isinstance(v, basestring) and
+                               number_re.match(v) is not None)
+
+
+def test_sequence():
+    """
+    Return true if the variable is a sequence. Sequences are variables
+    that are iterable.
+    """
+    def wrapped(environment, context, value):
+        try:
+            len(value)
+            value.__getitem__
+        except:
+            return False
+        return True
+    return wrapped
+
+
+def test_matching(regex):
+    r"""
+    Test if the variable matches the regular expression given. Note that
+    you have to escape special chars using *two* backslashes, these are
+    *not* raw strings.
+
+    .. sourcecode:: jinja
+
+        {% if var is matching @/^\d+$/ %}
+            var looks like a number
+        {% else %}
+            var doesn't really look like a number
+        {% endif %}
+    """
+    def wrapped(environment, context, value):
+        if type(regex) is regex_type:
+            regex_ = regex
+        else:
+            if environment.disable_regexps:
+                raise RuntimeError('regular expressions disabled.')
+            if isinstance(regex, unicode):
+                regex_ = re.compile(regex, re.U)
+            elif isinstance(regex, str):
+                regex_ = re.compile(regex)
+            else:
+                return False
+        return regex_.search(value) is not None
+    return wrapped
+
+
+def test_sameas(other):
+    """
+    Check if an object points to the same memory address than another
+    object:
+
+    .. sourcecode:: jinja
+
+        {% if foo.attribute is sameas(false) %}
+            the foo attribute really is the `False` singleton
+        {% endif %}
+
+    *New in Jinja 1.2*
+    """
+    return lambda e, c, v: v is other
+
+
+TESTS = {
+    'odd':              test_odd,
+    'even':             test_even,
+    'defined':          test_defined,
+    'lower':            test_lower,
+    'upper':            test_upper,
+    'numeric':          test_numeric,
+    'sequence':         test_sequence,
+    'matching':         test_matching,
+    'sameas':           test_sameas
+}
diff --git a/jinja2/translators/__init__.py b/jinja2/translators/__init__.py
new file mode 100644
index 0000000..ba55148
--- /dev/null
+++ b/jinja2/translators/__init__.py
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.translators
+    ~~~~~~~~~~~~~~~~~
+
+    The submodules of this module provide translators for the jinja ast.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+
+
+class Translator(object):
+    """
+    Base class of all translators.
+    """
+
+    def process(environment, tree, source=None):
+        """
+        Process the given ast with the rules defined in
+        environment and return a translated version of it.
+        The translated object can be anything. The python
+        translator for example outputs Template instances,
+        a javascript translator would probably output strings.
+
+        This is a static function.
+        """
+        pass
+    process = staticmethod(process)
diff --git a/jinja2/translators/python.py b/jinja2/translators/python.py
new file mode 100644
index 0000000..3e7b942
--- /dev/null
+++ b/jinja2/translators/python.py
@@ -0,0 +1,1115 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja.translators.python
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    This module translates a jinja ast into python code.
+
+    This translator tries hard to keep Jinja sandboxed. All security
+    relevant calls are wrapped by methods defined in the environment.
+    This affects:
+
+    - method calls
+    - attribute access
+    - name resolution
+
+    It also adds debug symbols used by the traceback toolkit implemented
+    in `jinja.utils`.
+
+    Implementation Details
+    ======================
+
+    It might sound strange but the translator tries to keep the generated
+    code readable as much as possible. This simplifies debugging the Jinja
+    core a lot. The additional processing overhead is just relevant for
+    the translation process, the additional comments and whitespace won't
+    appear in the saved bytecode.
+
+    :copyright: 2007 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import sys
+from jinja import nodes
+from jinja.nodes import get_nodes
+from jinja.parser import Parser
+from jinja.exceptions import TemplateSyntaxError
+from jinja.translators import Translator
+from jinja.datastructure import TemplateStream
+from jinja.utils import set, capture_generator
+
+
+#: regular expression for the debug symbols
+_debug_re = re.compile(r'^\s*\# DEBUG\(filename=(?P<filename>.*?), '
+                       r'lineno=(?P<lineno>\d+)\)$')
+
+# For Python versions without generator exit exceptions
+try:
+    GeneratorExit = GeneratorExit
+except NameError:
+    class GeneratorExit(Exception):
+        pass
+
+# For Pythons without conditional expressions
+try:
+    exec '0 if 0 else 0'
+    have_conditional_expr = True
+except SyntaxError:
+    have_conditional_expr = False
+
+
+class Template(object):
+    """
+    Represents a finished template.
+    """
+
+    def __init__(self, environment, code):
+        self.environment = environment
+        self.code = code
+        self.generate_func = None
+
+    def dump(self, stream=None):
+        """Dump the template into python bytecode."""
+        if stream is not None:
+            from marshal import dump
+            dump(self.code, stream)
+        else:
+            from marshal import dumps
+            return dumps(self.code)
+
+    def load(environment, data):
+        """Load the template from python bytecode."""
+        if isinstance(data, basestring):
+            from marshal import loads
+            code = loads(data)
+        else:
+            from marshal import load
+            code = load(data)
+        return Template(environment, code)
+    load = staticmethod(load)
+
+    def render(self, *args, **kwargs):
+        """Render a template."""
+        __traceback_hide__ = True
+        ctx = self._prepare(*args, **kwargs)
+        try:
+            return capture_generator(self.generate_func(ctx))
+        except:
+            self._debug(ctx, *sys.exc_info())
+
+    def stream(self, *args, **kwargs):
+        """Render a template as stream."""
+        def proxy(ctx):
+            try:
+                for item in self.generate_func(ctx):
+                    yield item
+            except GeneratorExit:
+                return
+            except:
+                self._debug(ctx, *sys.exc_info())
+        return TemplateStream(proxy(self._prepare(*args, **kwargs)))
+
+    def _prepare(self, *args, **kwargs):
+        """Prepare the template execution."""
+        # if there is no generation function we execute the code
+        # in a new namespace and save the generation function and
+        # debug information.
+        env = self.environment
+        if self.generate_func is None:
+            ns = {'environment': env}
+            exec self.code in ns
+            self.generate_func = ns['generate']
+        return env.context_class(env, *args, **kwargs)
+
+    def _debug(self, ctx, exc_type, exc_value, traceback):
+        """Debugging Helper"""
+        # just modify traceback if we have that feature enabled
+        from traceback import print_exception
+        print_exception(exc_type, exc_value, traceback)
+
+        if self.environment.friendly_traceback:
+            # hook the debugger in
+            from jinja.debugger import translate_exception
+            exc_type, exc_value, traceback = translate_exception(
+                self, ctx, exc_type, exc_value, traceback)
+        print_exception(exc_type, exc_value, traceback)
+
+        raise exc_type, exc_value, traceback
+
+
+class TranslationOperator(object):
+    """
+    A translation operator has a single string representing the operation
+    """
+    def __init__(self, operator, translator):
+        self.operator = operator
+        self.translator = translator
+
+class UnaryOperator(TranslationOperator):
+    """
+    A unary operator has one side to the operation.
+    """
+    def __call__(self, node):
+        """
+        Apply operator.
+        """
+        return '( %s %s)' % (
+            self.operator,
+            self.translator.handle_node(node.node),
+        )
+
+class BinaryOperator(TranslationOperator):
+    """
+    A binary operator has two sides to the operation.
+    """
+    def __call__(self, node):
+        """
+        Apply operator.
+        """
+        return '(%s %s %s)' % (
+            self.translator.handle_node(node.left),
+            self.operator,
+            self.translator.handle_node(node.right),
+        )
+
+class PythonTranslator(Translator):
+    """
+    Pass this translator a ast tree to get valid python code.
+    """
+
+    def __init__(self, environment, node, source):
+        self.environment = environment
+        self.loader = environment.loader.get_controlled_loader()
+        self.node = node
+        self.source = source
+        self.closed = False
+
+        #: current level of indention
+        self.indention = 0
+        #: each {% cycle %} tag has a unique ID which increments
+        #: automatically for each tag.
+        self.last_cycle_id = 0
+        #: set of used shortcuts jinja has to make local automatically
+        self.used_shortcuts = set(['undefined_singleton'])
+        #: set of used datastructures jinja has to import
+        self.used_data_structures = set()
+        #: set of used utils jinja has to import
+        self.used_utils = set()
+        #: flags for runtime error
+        self.require_runtime_error = False
+        #: do wee need a "set" object?
+        self.need_set_import = False
+        #: flag for regular expressions
+        self.compiled_regular_expressions = {}
+
+        #: bind the nodes to the callback functions. There are
+        #: some missing! A few are specified in the `unhandled`
+        #: mapping in order to disallow their usage, some of them
+        #: will not appear in the jinja parser output because
+        #: they are filtered out.
+        self.handlers = {
+            # block nodes
+            nodes.Template:                 self.handle_template,
+            nodes.Text:                     self.handle_template_text,
+            nodes.NodeList:                 self.handle_node_list,
+            nodes.ForLoop:                  self.handle_for_loop,
+            nodes.IfCondition:              self.handle_if_condition,
+            nodes.Cycle:                    self.handle_cycle,
+            nodes.Print:                    self.handle_print,
+            nodes.Macro:                    self.handle_macro,
+            nodes.Call:                     self.handle_call,
+            nodes.Set:                      self.handle_set,
+            nodes.Filter:                   self.handle_filter,
+            nodes.Block:                    self.handle_block,
+            nodes.Include:                  self.handle_include,
+            nodes.Trans:                    self.handle_trans,
+
+            # expression nodes
+            nodes.NameExpression:           self.handle_name,
+            nodes.CompareExpression:        self.handle_compare,
+            nodes.TestExpression:           self.handle_test,
+            nodes.ConstantExpression:       self.handle_const,
+            nodes.RegexExpression:          self.handle_regex,
+            nodes.SubscriptExpression:      self.handle_subscript,
+            nodes.FilterExpression:         self.handle_filter_expr,
+            nodes.CallExpression:           self.handle_call_expr,
+            nodes.AddExpression:            BinaryOperator('+', self),
+            nodes.SubExpression:            BinaryOperator('-', self),
+            nodes.ConcatExpression:         self.handle_concat,
+            nodes.DivExpression:            BinaryOperator('/', self),
+            nodes.FloorDivExpression:       BinaryOperator('//', self),
+            nodes.MulExpression:            BinaryOperator('*', self),
+            nodes.ModExpression:            BinaryOperator('%', self),
+            nodes.PosExpression:            UnaryOperator('+', self),
+            nodes.NegExpression:            UnaryOperator('-', self),
+            nodes.PowExpression:            BinaryOperator('**', self),
+            nodes.DictExpression:           self.handle_dict,
+            nodes.SetExpression:            self.handle_set_expr,
+            nodes.ListExpression:           self.handle_list,
+            nodes.TupleExpression:          self.handle_tuple,
+            nodes.UndefinedExpression:      self.handle_undefined,
+            nodes.AndExpression:            BinaryOperator(' and ', self),
+            nodes.OrExpression:             BinaryOperator(' or ', self),
+            nodes.NotExpression:            UnaryOperator(' not ', self),
+            nodes.SliceExpression:          self.handle_slice,
+            nodes.ConditionalExpression:    self.handle_conditional_expr
+        }
+
+    # -- public methods
+
+    def process(environment, node, source=None):
+        """
+        The only public method. Creates a translator instance,
+        translates the code and returns it in form of an
+        `Template` instance.
+        """
+        translator = PythonTranslator(environment, node, source)
+        filename = node.filename or '<template>'
+        source = translator.translate()
+        return Template(environment, compile(source, filename, 'exec'))
+    process = staticmethod(process)
+
+    # -- private helper methods
+
+    def indent(self, text):
+        """
+        Indent the current text. This does only indent the
+        first line.
+        """
+        return (' ' * (self.indention * 4)) + text
+
+    def to_tuple(self, args):
+        """
+        Return a tuple repr without nested repr.
+        """
+        return '(%s%s)' % (
+            ', '.join(args),
+            len(args) == 1 and ',' or ''
+        )
+
+    def nodeinfo(self, node, force=False):
+        """
+        Return a comment that helds the node informations or None
+        if there is no need to add a debug comment.
+        """
+        return '# DEBUG(filename=%s, lineno=%s)' % (
+            node.filename or '',
+            node.lineno
+        )
+
+    def handle_node(self, node):
+        """
+        Handle one node. Resolves the correct callback functions defined
+        in the callback mapping.
+        """
+        if self.closed:
+            raise RuntimeError('translator is closed')
+        if node.__class__ in self.handlers:
+            return self.handlers[node.__class__](node)
+        else:
+            raise AssertionError('unhandled node %r' % node.__class__)
+
+    def close(self):
+        """
+        Clean up stuff.
+        """
+        self.closed = True
+        self.handlers = self.node = self.environment = self.loader = None
+
+    def translate(self):
+        """
+        Translate the node defined in the constructor.
+        """
+        try:
+            return self.handle_node(self.node)
+        finally:
+            self.close()
+
+    # -- jinja nodes
+
+    def handle_template(self, node):
+        """
+        Handle the overall template node. This node is the first node and
+        ensures that we get the bootstrapping code. It also knows about
+        inheritance information. It only occours as outer node, never in
+        the tree itself.
+        """
+        self.indention = 1
+
+        # if there is a parent template we parse the parent template and
+        # update the blocks there. Once this is done we drop the current
+        # template in favor of the new one. Do that until we found the
+        # root template.
+        parent = None
+        overwrites = {}
+        blocks = {}
+        requirements = []
+        outer_filename = node.filename or '<template>'
+
+        # this set is required in order to not add blocks to the block
+        # dict a second time if they were not overridden in one template
+        # in the template chain.
+        already_registered_block = set()
+
+        while node.extends is not None:
+            # the direct child nodes in a template that are not blocks
+            # are processed as template globals, thus executed *before*
+            # the master layout template is loaded. This can be used
+            # for further processing. The output of those nodes does
+            # not appear in the final template.
+            requirements += [child for child in node.body.get_child_nodes()
+                             if child.__class__ not in (nodes.Text,
+                             nodes.Block)]
+
+            # load the template we inherit from and add not known blocks.
+            # this also marks the templates on the controlled loader but
+            # are never removed.  that's no problem because we don't allow
+            # parents we extend from as includes and the controlled loader
+            # is only used for this templated
+            parent = self.loader.parse(node.extends,
+                                       node.filename)
+
+            # look up all block nodes in the current template and
+            # add them to the override dict.
+            for n in get_nodes(nodes.Block, node):
+                overwrites[n.name] = n
+            # handle direct overrides
+            for n in get_nodes(nodes.Block, parent):
+                # an overwritten block for the parent template. handle that
+                # override in the template and register it in the deferred
+                # block dict.
+                if n.name in overwrites and n not in already_registered_block:
+                    blocks.setdefault(n.name, []).append(n.clone())
+                    n.replace(overwrites[n.name])
+                    already_registered_block.add(n)
+            # make the parent node the new node
+            node = parent
+
+        # handle requirements code
+        if requirements:
+            requirement_lines = ['def bootstrap(context):']
+            for n in requirements:
+                requirement_lines.append(self.handle_node(n))
+            requirement_lines.append('    if 0: yield None\n')
+
+        # handle body in order to get the used shortcuts
+        body_code = self.handle_node(node.body)
+
+        # same for blocks in callables
+        block_lines = []
+        block_items = blocks.items()
+        block_items.sort()
+        dict_lines = []
+        for name, items in block_items:
+            tmp = []
+            for idx, item in enumerate(items):
+                # ensure that the indention is correct
+                self.indention = 1
+                func_name = 'block_%s_%s' % (name, idx)
+                data = self.handle_block(item, idx + 1)
+                # blocks with data
+                if data:
+                    block_lines.extend([
+                        'def %s(context):' % func_name,
+                        self.indent(self.nodeinfo(item, True)),
+                        data,
+                        '    if 0: yield None\n'
+                    ])
+                    tmp.append('buffereater(%s)' % func_name)
+                    self.used_utils.add('buffereater')
+                # blocks without data, can default to something
+                # from utils
+                else:
+                    tmp.append('empty_block')
+                    self.used_utils.add('empty_block')
+            dict_lines.append('    %r: %s' % (
+                str(name),
+                self.to_tuple(tmp)
+            ))
+
+        # bootstrapping code
+        lines = ['# Essential imports', 'from __future__ import division']
+        if self.used_utils:
+            lines.append('from jinja.utils import %s' % \
+                         ', '.join(tuple(self.used_utils)))
+        if self.require_runtime_error:
+            lines.append('from jinja.exceptions import TemplateRuntimeError')
+        if self.used_data_structures:
+            lines.append('from jinja.datastructure import %s' % ', '.
+                         join(self.used_data_structures))
+        if self.need_set_import:
+            lines.append('from jinja.utils import set')
+
+        # compile regular expressions
+        if self.compiled_regular_expressions:
+            lines.append('import re')
+            lines.append('\n# Compile used regular expressions')
+            for regex, name in self.compiled_regular_expressions.iteritems():
+                lines.append('%s = re.compile(%r)' % (name, regex))
+
+        lines.append(
+            '\n# Aliases for some speedup\n'
+            '%s\n\n'
+            '# Marker for Jinja templates\n'
+            '__jinja_template__ = True\n\n'
+            '# Name for disabled debugging\n'
+            '__name__ = %r\n\n'
+            'def generate(context):\n'
+            '    assert environment is context.environment' % (
+                '\n'.join([
+                    '%s = environment.%s' % (item, item) for item in
+                    self.used_shortcuts
+                ]),
+                outer_filename
+            )
+        )
+
+        # the template body
+        if requirements:
+            lines.append('    for item in bootstrap(context): pass')
+        lines.append(body_code)
+        lines.append('    if 0: yield None\n')
+
+        # now write the bootstrapping (requirements) core if there is one
+        if requirements:
+            lines.append('# Bootstrapping code')
+            lines.extend(requirement_lines)
+
+        # blocks must always be defined. even if it's empty. some
+        # features depend on it
+        if block_lines:
+            lines.append('# Superable blocks')
+            lines.extend(block_lines)
+        lines.append('# Block mapping')
+        if dict_lines:
+            lines.append('blocks = {\n%s\n}\n' % ',\n'.join(dict_lines))
+        else:
+            lines.append('blocks = {}\n')
+
+        # now get the real source lines and map the debugging symbols
+        debug_mapping = []
+        file_mapping = {}
+        last = None
+        offset = -1
+        sourcelines = ('\n'.join(lines)).splitlines()
+        result = []
+
+        for idx, line in enumerate(sourcelines):
+            m = _debug_re.search(line)
+            if m is not None:
+                d = m.groupdict()
+                filename = d['filename'] or None
+                if isinstance(filename, unicode):
+                    filename = filename.encode('utf-8')
+                if filename in file_mapping:
+                    file_id = file_mapping[filename]
+                else:
+                    file_id = file_mapping[filename] = 'F%d' % \
+                                                       len(file_mapping)
+                this = (file_id, int(d['lineno']))
+                # if it's the same as the line before we ignore it
+                if this != last:
+                    debug_mapping.append('(%r, %s, %r)' % ((idx - offset,) + this))
+                    last = this
+                # for each debug symbol the line number and so the offset
+                # changes by one.
+                offset += 1
+            else:
+                result.append(line)
+
+        # now print file mapping and debug info
+        # the debug info:
+        #   debug_info          binds template line numbers to generated
+        #                       source lines. this information is always
+        #                       present and part of the bytecode.
+        #   template_source     only available if loaded from string to
+        #                       get debug source code. Because this is
+        #                       dumped too it's a bad idea to dump templates
+        #                       loaded from a string.
+        result.append('\n# Debug Information')
+        file_mapping = file_mapping.items()
+        file_mapping.sort(lambda a, b: cmp(a[1], b[1]))
+        for filename, file_id in file_mapping:
+            result.append('%s = %r' % (file_id, filename))
+        result.append('debug_info = %s' % self.to_tuple(debug_mapping))
+        result.append('template_source = %r' % self.source)
+
+        return '\n'.join(result)
+
+    def handle_template_text(self, node):
+        """
+        Handle data around nodes.
+        """
+        # special case: no variables
+        if not node.variables:
+            return self.indent(self.nodeinfo(node)) + '\n' + \
+                   self.indent('yield %r' % node.text.replace('%%', '%'))
+
+        # special case: one variable, no text
+        self.used_shortcuts.add('finish_var')
+        if len(node.variables) == 1 and node.text == '%s':
+            return self.indent(self.nodeinfo(node)) + '\n' + \
+                   self.indent('yield finish_var(%s, context)' %
+                               self.handle_node(node.variables[0]))
+
+        # all other cases
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+
+        write(self.nodeinfo(node))
+        write('yield %r %% (' % node.text)
+        self.indention += 1
+        for var in node.variables:
+            write(self.nodeinfo(var))
+            write('finish_var(%s, context)' % self.handle_node(var) + ',')
+        self.indention -= 1
+        write(')')
+
+        return '\n'.join(buf)
+
+    def handle_node_list(self, node):
+        """
+        In some situations we might have a node list. It's just
+        a collection of multiple statements.
+
+        If the nodelist was empty it will return an empty string
+        """
+        body = '\n'.join([self.handle_node(n) for n in node])
+        if body:
+            return self.indent(self.nodeinfo(node)) + '\n' + body
+        return ''
+
+    def handle_for_loop(self, node):
+        """
+        Handle a for loop. Pretty basic, just that we give the else
+        clause a different behavior.
+        """
+        self.used_data_structures.add('LoopContext')
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+        write(self.nodeinfo(node))
+        write('context.push()')
+
+        # recursive loops
+        if node.recursive:
+            write('def loop(seq):')
+            self.indention += 1
+            write('for %s in context[\'loop\'].push(seq):' %
+                self.handle_node(node.item),
+            )
+
+        # simple loops
+        else:
+            write('context[\'loop\'] = loop = LoopContext(%s, '
+                  'context[\'loop\'], None)' % self.handle_node(node.seq))
+            write('for %s in loop:' %
+                self.handle_node(node.item)
+            )
+
+        # handle real loop code
+        self.indention += 1
+        write(self.nodeinfo(node.body))
+        if node.body:
+            buf.append(self.handle_node(node.body))
+        else:
+            write('pass')
+        self.indention -= 1
+
+        # else part of loop
+        if node.else_:
+            write('if not context[\'loop\'].iterated:')
+            self.indention += 1
+            write(self.nodeinfo(node.else_))
+            buf.append(self.handle_node(node.else_) or self.indent('pass'))
+            self.indention -= 1
+
+        # call recursive for loop!
+        if node.recursive:
+            write('context[\'loop\'].pop()')
+            write('if 0: yield None')
+            self.indention -= 1
+            write('context[\'loop\'] = LoopContext(None, context[\'loop\'], '
+                  'buffereater(loop))')
+            self.used_utils.add('buffereater')
+            write('for item in loop(%s):' % self.handle_node(node.seq))
+            self.indention += 1
+            write('yield item')
+            self.indention -= 1
+
+        write('context.pop()')
+        return '\n'.join(buf)
+
+    def handle_if_condition(self, node):
+        """
+        Handle an if condition node.
+        """
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+        write(self.nodeinfo(node))
+        for idx, (test, body) in enumerate(node.tests):
+            write('%sif %s:' % (
+                idx and 'el' or '',
+                self.handle_node(test)
+            ))
+            self.indention += 1
+            write(self.nodeinfo(body))
+            buf.append(self.handle_node(body) or self.indent('pass'))
+            self.indention -= 1
+        if node.else_ is not None:
+            write('else:')
+            self.indention += 1
+            write(self.nodeinfo(node.else_))
+            buf.append(self.handle_node(node.else_) or self.indent('pass'))
+            self.indention -= 1
+        return '\n'.join(buf)
+
+    def handle_cycle(self, node):
+        """
+        Handle the cycle tag.
+        """
+        self.used_data_structures.add('CycleContext')
+        name = '::cycle_%x' % self.last_cycle_id
+        self.last_cycle_id += 1
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+
+        write('if %r not in context.current:' % name)
+        self.indention += 1
+        write(self.nodeinfo(node))
+        if node.seq.__class__ in (nodes.TupleExpression,
+                                  nodes.ListExpression):
+            write('context.current[%r] = CycleContext(%s)' % (
+                name,
+                self.to_tuple([self.handle_node(n) for n in node.seq.items])
+            ))
+            hardcoded = True
+        else:
+            write('context.current[%r] = CycleContext()' % name)
+            hardcoded = False
+        self.indention -= 1
+
+        self.used_shortcuts.add('finish_var')
+        if hardcoded:
+            write('yield finish_var(context.current[%r].cycle(), '
+                  'context)' % name)
+        else:
+            write('yield finish_var(context.current[%r].cycle(%s), '
+                  'context)' % (
+                name,
+                self.handle_node(node.seq)
+            ))
+
+        return '\n'.join(buf)
+
+    def handle_print(self, node):
+        """
+        Handle a print statement.
+        """
+        self.used_shortcuts.add('finish_var')
+        return self.indent(self.nodeinfo(node)) + '\n' +\
+               self.indent('yield finish_var(%s, context)' %
+                           self.handle_node(node.expr))
+
+    def handle_macro(self, node):
+        """
+        Handle macro declarations.
+        """
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+
+        write('def macro(*args, **kw):')
+        self.indention += 1
+        write(self.nodeinfo(node))
+
+        # collect macro arguments
+        arg_items = []
+        caller_overridden = False
+
+        # if we have conditional expressions available in that python
+        # build (for example cpython > 2.4) we can use them, they
+        # will perform slightly better.
+        if have_conditional_expr:
+            arg_tmpl = '\'%(name)s\': args[%(pos)d] if argcount > %(pos)d ' \
+                       'else %(default)s'
+        # otherwise go with the and/or tuple hack:
+        else:
+            arg_tmpl = '\'%(name)s\': (argcount > %(pos)d and '\
+                       '(args[%(pos)d],) or (%(default)s,))[0]'
+
+        if node.arguments:
+            varargs_init = '\'varargs\': args[%d:]' % len(node.arguments)
+            write('argcount = len(args)')
+            for idx, (name, n) in enumerate(node.arguments):
+                arg_items.append(arg_tmpl % {
+                    'name':     name,
+                    'pos':      idx,
+                    'default':  n is None and 'undefined_singleton' or
+                                self.handle_node(n)
+                })
+                if name == 'caller':
+                    caller_overridden = True
+                elif name == 'varargs':
+                    varargs_init = None
+        else:
+            varargs_init = '\'varargs\': args'
+
+        if caller_overridden:
+            write('kw.pop(\'caller\', None)')
+        else:
+            arg_items.append('\'caller\': kw.pop(\'caller\', undefined_singleton)')
+        if varargs_init:
+            arg_items.append(varargs_init)
+
+        write('context.push({%s})' % ',\n              '.join([
+            idx and self.indent(item) or item for idx, item
+            in enumerate(arg_items)
+        ]))
+
+        # disallow any keyword arguments
+        write('if kw:')
+        self.indention += 1
+        write('raise TemplateRuntimeError(\'%s got an unexpected keyword '
+              'argument %%r\' %% iter(kw).next())' % node.name)
+        self.require_runtime_error = True
+        self.indention -= 1
+
+        write(self.nodeinfo(node.body))
+        data = self.handle_node(node.body)
+        if data:
+            buf.append(data)
+        write('context.pop()')
+        write('if 0: yield None')
+        self.indention -= 1
+        buf.append(self.indent('context[%r] = buffereater(macro, True)' %
+                               node.name))
+        self.used_utils.add('buffereater')
+
+        return '\n'.join(buf)
+
+    def handle_call(self, node):
+        """
+        Handle extended macro calls.
+        """
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+
+        write('def call(**kwargs):')
+        self.indention += 1
+        write('context.push(kwargs)')
+        data = self.handle_node(node.body)
+        if data:
+            buf.append(data)
+        write('context.pop()')
+        write('if 0: yield None')
+        self.indention -= 1
+        write('yield ' + self.handle_call_expr(node.expr,
+              {'caller': 'buffereater(call)'}))
+        self.used_utils.add('buffereater')
+
+        return '\n'.join(buf)
+
+    def handle_set(self, node):
+        """
+        Handle variable assignments.
+        """
+        if node.scope_local:
+            tmpl = 'context[%r] = %s'
+        else:
+            tmpl = 'context.set_nonlocal(%r, %s)'
+        return self.indent(self.nodeinfo(node)) + '\n' + \
+               self.indent(tmpl % (
+            node.name,
+            self.handle_node(node.expr)
+        ))
+
+    def handle_filter(self, node):
+        """
+        Handle filter sections.
+        """
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+        write('def filtered():')
+        self.indention += 1
+        write('context.push()')
+        write(self.nodeinfo(node.body))
+        data = self.handle_node(node.body)
+        if data:
+            buf.append(data)
+        write('context.pop()')
+        write('if 0: yield None')
+        self.indention -= 1
+        self.used_shortcuts.add('apply_filters')
+        write('yield apply_filters(buffereater(filtered)(), context, %s)' %
+            self.to_tuple(['(%r, %s)' % (
+                name,
+                self.to_tuple(map(self.handle_node, args))
+            ) for name, args in node.filters])
+        )
+        self.used_utils.add('buffereater')
+        return '\n'.join(buf)
+
+    def handle_block(self, node, level=0):
+        """
+        Handle blocks in the sourcecode. We only use them to
+        call the current block implementation that is stored somewhere
+        else.
+        """
+        rv = self.handle_node(node.body)
+        if not rv:
+            return ''
+
+        self.used_data_structures.add('SuperBlock')
+        buf = []
+        write = lambda x: buf.append(self.indent(x))
+
+        write(self.nodeinfo(node))
+        write('context.push({\'super\': SuperBlock(%r, blocks, %r, context)})' % (
+            str(node.name),
+            level
+        ))
+        write(self.nodeinfo(node.body))
+        buf.append(rv)
+        write('context.pop()')
+        return '\n'.join(buf)
+
+    def handle_include(self, node):
+        """
+        Include another template at the current position.
+        """
+        tmpl = self.loader.parse(node.template,
+                                 node.filename)
+        try:
+            return self.handle_node(tmpl.body)
+        finally:
+            self.loader.mark_as_processed()
+
+    def handle_trans(self, node):
+        """
+        Handle translations.
+        """
+        if node.replacements:
+            replacements = []
+            for name, n in node.replacements.iteritems():
+                replacements.append('%r: %s' % (
+                    name,
+                    self.handle_node(n)
+                ))
+            replacements = '{%s}' % ', '.join(replacements)
+        else:
+            replacements = 'None'
+        return self.indent(self.nodeinfo(node)) + '\n' +\
+               self.indent('yield context.translate_func(%r, %r, %r, %s)' % (
+            node.singular,
+            node.plural,
+            node.indicator,
+            replacements
+        ))
+
+    # -- python nodes
+
+    def handle_name(self, node):
+        """
+        Handle name assignments and name retreivement.
+        """
+        if node.name == '_':
+            return 'context.translate_func'
+        return 'context[%r]' % node.name
+
+    def handle_compare(self, node):
+        """
+        Any sort of comparison
+        """
+        ops = {
+            'eq':       '==',
+            'ne':       '!=',
+            'lt':       '<',
+            'lteq':     '<=',
+            'gt':       '>',
+            'gteq':     '>=',
+            'in':       'in',
+            'not in':   'not in'
+        }
+        buf = []
+        buf.append(self.handle_node(node.expr))
+        for op, n in node.ops:
+            buf.append(ops[op])
+            buf.append(self.handle_node(n))
+        return ' '.join(buf)
+
+    def handle_test(self, node):
+        """
+        Handle test calls.
+        """
+        self.used_shortcuts.add('perform_test')
+        return 'perform_test(context, %r, %s, %s)' % (
+            node.name,
+            self.to_tuple([self.handle_node(n) for n in node.args]),
+            self.handle_node(node.node)
+        )
+
+    def handle_const(self, node):
+        """
+        Constant values in expressions.
+        """
+        return repr(node.value)
+
+    def handle_regex(self, node):
+        """
+        Regular expression literals.
+        """
+        if self.environment.disable_regexps:
+            raise TemplateSyntaxError('regular expressions disabled.')
+        if node.value in self.compiled_regular_expressions:
+            return self.compiled_regular_expressions[node.value]
+        name = 'regex_%d' % len(self.compiled_regular_expressions)
+        self.compiled_regular_expressions[node.value] = name
+        return name
+
+    def handle_subscript(self, node):
+        """
+        Handle variable based attribute access foo['bar'].
+        """
+        self.used_shortcuts.add('get_attribute')
+        if node.arg.__class__ is nodes.SliceExpression:
+            rv = self.handle_slice(node.arg, getslice_test=True)
+            if rv is not None:
+                return self.handle_node(node.node) + rv
+        return 'get_attribute(%s, %s)' % (
+            self.handle_node(node.node),
+            self.handle_node(node.arg)
+        )
+
+    def handle_tuple(self, node):
+        """
+        Tuple unpacking loops.
+        """
+        return self.to_tuple([self.handle_node(n) for n in node.items])
+
+    def handle_filter_expr(self, node):
+        """
+        We use the pipe operator for filtering.
+        """
+        self.used_shortcuts.add('apply_filters')
+        return 'apply_filters(%s, context, %s)' % (
+            self.handle_node(node.node),
+            self.to_tuple(['(%r, %s)' % (
+                name,
+                self.to_tuple(map(self.handle_node, args))
+            ) for name, args in node.filters])
+        )
+
+    def handle_call_expr(self, node, extra_kwargs=None):
+        """
+        Handle function calls.
+        """
+        args = []
+        kwargs = {}
+        dyn_args = dyn_kwargs = None
+        if node.dyn_args is not None:
+            dyn_args = self.handle_node(node.dyn_args)
+        if node.dyn_kwargs is not None:
+            dyn_kwargs = self.handle_node(node.dyn_kwargs)
+        for arg in node.args:
+            args.append(self.handle_node(arg))
+        for name, arg in node.kwargs:
+            kwargs[name] = self.handle_node(arg)
+        if extra_kwargs:
+            kwargs.update(extra_kwargs)
+        if not (args or kwargs or dyn_args or dyn_kwargs):
+            self.used_shortcuts.add('call_function_simple')
+            return 'call_function_simple(%s, context)' % \
+                   self.handle_node(node.node)
+        self.used_shortcuts.add('call_function')
+        return 'call_function(%s, context, %s, {%s}, %s, %s)' % (
+            self.handle_node(node.node),
+            self.to_tuple(args),
+            ', '.join(['%r: %s' % i for i in kwargs.iteritems()]),
+            dyn_args,
+            dyn_kwargs
+        )
+
+    def handle_concat(self, node):
+        """
+        Convert some objects to unicode and concatenate them.
+        """
+        self.used_shortcuts.add('to_unicode')
+        return "u''.join(%s)" % self.to_tuple([
+            'to_unicode(%s)' % self.handle_node(arg)
+            for arg in node.args
+        ])
+
+
+    def handle_dict(self, node):
+        """
+        Dict constructor syntax.
+        """
+        return '{%s}' % ', '.join([
+            '%s: %s' % (
+                self.handle_node(key),
+                self.handle_node(value)
+            ) for key, value in node.items
+        ])
+
+    def handle_set_expr(self, node):
+        """
+        Set constructor syntax.
+        """
+        self.need_set_import = True
+        return 'set([%s])' % ', '.join([self.handle_node(n)
+                                        for n in node.items])
+
+    def handle_list(self, node):
+        """
+        We don't know tuples, tuples are lists for jinja.
+        """
+        return '[%s]' % ', '.join([
+            self.handle_node(n) for n in node.items
+        ])
+
+    def handle_undefined(self, node):
+        """
+        Return the current undefined literal.
+        """
+        return 'undefined_singleton'
+
+
+    def handle_slice(self, node, getslice_test=False):
+        """
+        Slice access. Because of backwards compatibilty to python's
+        `__getslice__` this function takes a second parameter that lets this
+        method return a regular slice bracket call. If a regular slice bracket
+        call that is compatible to __getslice__ is not possible the return
+        value will be `None` so that a regular `get_attribute` wrapping can
+        happen.
+        """
+        if node.start is None:
+            start = not getslice_test and 'None' or ''
+        else:
+            start = self.handle_node(node.start)
+        if node.stop is None:
+            stop = not getslice_test and 'None' or ''
+        else:
+            stop = self.handle_node(node.stop)
+        if node.step is None:
+            step = 'None'
+        else:
+            if getslice_test:
+                return
+            step = self.handle_node(node.step)
+        if getslice_test:
+            return '[%s:%s]' % (start, stop)
+        return 'slice(%s, %s, %s)' % (start, stop, step)
+
+    def handle_conditional_expr(self, node):
+        """
+        Handle conditional expressions.
+        """
+        if have_conditional_expr:
+            tmpl = '%(expr1)s if %(test)s else %(expr2)s'
+        else:
+            tmpl = '(%(test)s and (%(expr1)s,) or (%(expr2)s,))[0]'
+        return tmpl % {
+            'test':     self.handle_node(node.test),
+            'expr1':    self.handle_node(node.expr1),
+            'expr2':    self.handle_node(node.expr2)
+        }
diff --git a/jinja2/utils.py b/jinja2/utils.py
new file mode 100644
index 0000000..3f6395e
--- /dev/null
+++ b/jinja2/utils.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+"""
+    jinja2.utils
+    ~~~~~~~~~~~~
+
+    Utility functions.
+
+    :copyright: 2008 by Armin Ronacher.
+    :license: BSD, see LICENSE for more details.
+"""