Index: markup/template.py
===================================================================
--- markup/template.py	(revision 277)
+++ markup/template.py	(working copy)
@@ -62,7 +62,7 @@
     """
 
     def __init__(self, name, filename='<string>', lineno=-1):
-        message = 'bad directive "%s"' % name.localname
+        message = 'bad directive "%s"' % name
         TemplateSyntaxError.__init__(self, message, filename, lineno)
 
 
@@ -811,7 +811,7 @@
                 if tag in self.NAMESPACE:
                     cls = self._dir_by_name.get(tag.localname)
                     if cls is None:
-                        raise BadDirectiveError(tag, pos[0], pos[1])
+                        raise BadDirectiveError(tag.localname, pos[0], pos[1])
                     value = attrib.get(getattr(cls, 'ATTRIBUTE', None), '')
                     directives.append(cls(value, *pos))
                     strip = True
@@ -821,7 +821,8 @@
                     if name in self.NAMESPACE:
                         cls = self._dir_by_name.get(name.localname)
                         if cls is None:
-                            raise BadDirectiveError(name, pos[0], pos[1])
+                            raise BadDirectiveError(name.localname, pos[0],
+                                                    pos[1])
                         directives.append(cls(value, *pos))
                     else:
                         if value:
@@ -1078,6 +1079,91 @@
 SUB = Template.SUB
 
 
+class TextTemplate(Template):
+    """
+    >>> tmpl = TextTemplate('''Dear $name,
+    ... 
+    ... We have the following items for you:
+    ... #for item in items
+    ...  * $item
+    ... #endfor
+    ... 
+    ... All the best,
+    ... Foobar
+    ... #endif''')
+    >>> print tmpl.generate(name='Joe', items=[1, 2, 3]).render('text')
+    Dear Joe,
+    <BLANKLINE>
+    We have the following items for you:
+     * 1
+     * 2
+     * 3
+    <BLANKLINE>
+    All the best,
+    Foobar
+    <BLANKLINE>
+    """
+
+    directives = [('def', DefDirective),
+                  ('when', WhenDirective),
+                  ('otherwise', OtherwiseDirective),
+                  ('for', ForDirective),
+                  ('if', IfDirective),
+                  ('choose', ChooseDirective),
+                  ('with', WithDirective)]
+    _dir_by_name = dict(directives)
+    _dir_order = [directive[1] for directive in directives]
+
+    _directive_re = re.compile('^#(\w+.*)\n?', re.MULTILINE)
+
+    def parse(self):
+        stream = [] # list of events of the "compiled" template
+        dirmap = {} # temporary mapping of directives to elements
+        depth = 0
+
+        source = self.source.read()
+        offset = 0
+        lineno = 1
+        for idx, mo in enumerate(self._directive_re.finditer(source)):
+            start, end = mo.span()
+            if start > offset:
+                text = source[offset:start]
+                for kind, data, pos in self._interpolate(text, self.filename, lineno, 0):
+                    stream.append((kind, data, pos))
+                lineno += len(text.splitlines())
+
+            text = source[start + 1:end]
+            lineno += len(text.splitlines())
+            directive = text.split(None, 1)
+            if len(directive) > 1:
+                command, value = directive
+            else:
+                command, value = directive[0], None
+
+            if not command.startswith('end'):
+                cls = self._dir_by_name.get(command)
+                if cls is None:
+                    raise BadDirectiveError(command)
+                directive = cls(value, self.filename, lineno, 0)
+                dirmap[depth] = (directive, len(stream))
+                depth += 1
+            else:
+                depth -= 1
+                command = command[3:]
+                if depth in dirmap:
+                    directive, start_offset = dirmap.pop(depth)
+                    substream = stream[start_offset:]
+                    stream[start_offset:] = [(SUB, ([directive], substream),
+                                              (self.filename, lineno, 0))]
+
+            offset = end
+
+        return stream
+
+    def _match(self, stream, ctxt=None):
+        return stream
+
+
 class TemplateLoader(object):
     """Responsible for loading templates from files on the specified search
     path.
