""" template - simple template using wiki macro syntax

The template does not contain any logic, just static source and macro
calls whenever you need dynamic data

Each template has a controller object, that must support all macro calls
in runtime. The controller decide if an item should print, and it can
call model objects to get the data needed for the template.

The template is parsed and formated as python code thats write to
out. Each macro call is formated as python code thats write the output
of the call to controller.macro_name

The template could be pickled or cached in memory.
"""

import re


class Parser:
    """ Minimal parser with wiki macro support """
    macro_re = re.compile(r'\[\[(?P<name>.+?)\]\]', re.MULTILINE)

    def __init__(self, formatter):
        self.formatter = formatter

    def parse(self, text):
        location = 0
        for match in self.macro_re.finditer(text):
            # Format text up to match
            self.formatter.text(text[location:match.start()])
            # Format match
            self.formatter.macro(match.group('name'))
            location = match.end()    
        # Format rest
        self.formatter.text(text[location:])


class Formatter:
    """ text_python like formatter """
    def __init__(self):
        self._output = []

    def text(self, text):
        """ Create python code that write text when run """
        self._output.append('out.write("""%s""")' % text)
        
    def macro(self, name):
        """ Create Python code that prints function output """
        self._output.append('out.write(controller.macro_%s())' % name)
        
    def output(self):
        return '\n'.join(self._output)


class Template:
    """ Compiled template """
    def __init__(self, path):
        self.path = path
        
    def compile(self):
        template = file(self.path).read()
        template = template.strip()
        formatter = Formatter()
        parser = Parser(formatter)
        parser.parse(template)
        self.code = compile(formatter.output(), '<string>', 'exec')
        
    def render(self, controller, out):
        exec self.code
        

import os
import sys

class Controller:
    def macro_title(self):
        return "Quick Test"
    def macro_author(self):
        return os.environ.get('USER', 'unknown author')
    def macro_filelist(self):
        items = ['<li>%s</li>' % item for item in os.listdir(os.getcwd())
                 if not item.startswith('.')]
        items.sort()
        return '\n'.join(items)
                


if __name__ == '__main__':
    t = Template('test.txt')
    t.compile()
    t.render(Controller(), sys.stdout)
    
