1. Andriy Kornatskyy
  2. wheezy.template

Commits

Andriy Kornatskyy  committed 45911e9

Introduced builder module.

  • Participants
  • Parent commits 497dfb4
  • Branches default

Comments (0)

Files changed (4)

File src/wheezy/template/builder.py

View file
+
+"""
+"""
+
+
+def builder_scan(extensions):
+    builder_rules = {}
+    for extension in extensions:
+        if hasattr(extension, 'builder_rules'):
+            rules = extension.builder_rules
+            for token, builder in rules:
+                builder_rules.setdefault(token, []).append(builder)
+    return builder_rules
+
+
+class BlockBuilder(object):
+
+    def __init__(self, rules, indent='', lineno=0):
+        self.rules = rules
+        self.indent = indent
+        self.lineno = lineno
+        self.buf = []
+
+    def start_block(self):
+        self.indent += '    '
+
+    def end_block(self):
+        self.indent = self.indent[:-4]
+
+    def add(self, lineno, code):
+        if lineno < self.lineno:
+            raise ValueError('Inconsistence at %s : %s' %
+                    (self.lineno, lineno))
+        if lineno == self.lineno:
+            line = self.buf[-1]
+            if line:
+                self.buf[-1] = line + '; ' + code
+            else:
+                self.buf[-1] = code
+        else:
+            pad = lineno - self.lineno - 1
+            if pad > 0:
+                self.buf.extend([''] * pad)
+            self.buf.append(self.indent + code)
+        self.lineno = lineno + code.count('\n')
+
+    def build_block(self, nodes):
+        for lineno, token, value in nodes:
+            self.build_token(lineno, token, value)
+
+    def build_token(self, lineno, token, value):
+        if token in self.rules:
+            for rule in self.rules[token]:
+                if rule(self, lineno, token, value):
+                    break
+        else:
+            raise ValueError('No rule to build "%s" token.' % token)
+
+    def to_string(self):
+        return '\n'.join(self.buf)
+
+
+class SourceBuilder(object):
+
+    def __init__(self, rules, offset=2):
+        self.rules = rules
+        self.lineno = 0 - offset
+
+    def build_source(self, nodes):
+        block_builder = BlockBuilder(self.rules)
+        block_builder.build_block(nodes)
+        return block_builder.to_string()
+
+    def build_render(self, nodes):
+        block_builder = BlockBuilder(self.rules, lineno=self.lineno)
+        block_builder.build_token(self.lineno + 1, 'render', nodes)
+        return block_builder.to_string()
+
+    def build_extends(self, name, nodes):
+        block_builder = BlockBuilder(self.rules, lineno=self.lineno)
+        block_builder.build_token(self.lineno + 1, 'extends',
+                (name, nodes))
+        return block_builder.to_string()

File src/wheezy/template/engine.py

View file
 """
 """
 
+from wheezy.template.builder import SourceBuilder
+from wheezy.template.builder import builder_scan
 from wheezy.template.lexer import Lexer
 from wheezy.template.lexer import lexer_scan
 from wheezy.template.parser import Parser
         self.lexer = Lexer(lexer_rules, preprocessors)
         parser_rules, parser_configs = parser_scan(extensions)
         self.parser = Parser(parser_rules, parser_configs)
+        builder_rules = builder_scan(extensions)
+        self.builder = SourceBuilder(builder_rules)

File src/wheezy/template/ext/core.py

View file
 
 
 def parse_markup(value):
-    return '"""' + repr(value.replace('\\\n', ''))[1:-1] + '"""'
+    return repr(value.replace('\\\n', ''))
+
+
+# region: block_builders
+
+def build_render(builder, lineno, token, nodes):
+    assert lineno <= -1
+    builder.add(lineno, 'def render(ctx, local_defs, super_defs):')
+    builder.start_block()
+    builder.add(lineno + 1, '_b = []; w = _b.append')
+    builder.build_block(nodes)
+    lineno = builder.lineno
+    builder.add(lineno + 1, "return ''.join(_b)")
+
+
+def build_extends(builder, lineno, token, value):
+    assert lineno <= -1
+    extends, nodes = value
+    builder.add(lineno, 'def render(ctx, local_defs, super_defs):')
+    builder.start_block()
+    builder.add(lineno + 1, '_b = []; w = _b.append')
+    builder.build_block(nodes)
+    lineno = builder.lineno
+    builder.add(lineno + 1,
+            "return includes[%s](ctx, local_defs, super_defs)" % extends)
+
+
+def build_out(builder, lineno, token, nodes):
+    for lineno, token, value in nodes:
+        builder.add(lineno, 'w(%s)' % value)
+
+
+def build_def(builder, lineno, token, value):
+    stmt, nodes = value
+    def_name = stmt[4:stmt.index('(', 5)]
+    builder.add(lineno, stmt)
+    builder.start_block()
+    builder.add(lineno + 1, '_b = []; w = _b.append')
+    builder.build_block(nodes)
+    lineno = builder.lineno
+    builder.add(lineno, "return ''.join(_b)")
+    builder.end_block()
+    builder.add(lineno + 1, "local_defs['%s'] = %s" % tuple(
+                    [def_name] * 2))
+    return True
+
+
+def build_compound(builder, lineno, token, value):
+    stmt, nodes = value
+    builder.add(lineno, stmt)
+    builder.start_block()
+    builder.build_block(nodes)
+    builder.end_block()
 
 
 # region: core extension
     }
 
     parser_configs = [configure_parser]
+
+    builder_rules = [
+            ('render', build_render),
+            ('extends', build_extends),
+            ('def', build_def),
+            ('out', build_out),
+            ('if', build_compound),
+            ('elif', build_compound),
+            ('else', build_compound),
+    ]

File src/wheezy/template/ext/tests/test_core.py

View file
  Welcome, @name!
 """)
         assert [(1, 'out', [
-                    (1, 'markup', '"""\\n Welcome, """'),
+                    (1, 'markup', "'\\n Welcome, '"),
                     (2, 'var', 'name'),
-                    (2, 'markup', '"""!\\n"""')
+                    (2, 'markup', "'!\\n'")
                 ])] == nodes
+
+
+class BuilderTestCase(unittest.TestCase):
+    """ Test the ``CoreExtension`` generators.
+    """
+
+    def setUp(self):
+        from wheezy.template.engine import Engine
+        from wheezy.template.ext.core import CoreExtension
+        self.engine = Engine(extensions=[CoreExtension()])
+
+    def build_source(self, source):
+        nodes = list(self.engine.parser.parse(
+                    self.engine.lexer.tokenize(source)))
+        return self.engine.builder.build_source(nodes)
+
+    def build_render(self, source):
+        nodes = list(self.engine.parser.parse(
+                    self.engine.lexer.tokenize(source)))
+        return self.engine.builder.build_render(nodes)
+
+    def build_extends(self, name, source):
+        nodes = list(self.engine.parser.parse(
+                    self.engine.lexer.tokenize(source)))
+        return self.engine.builder.build_extends(name, nodes)
+
+    def test_out(self):
+        """ Test build_out.
+        """
+        assert "w('Welcome, '); w(username); w('!')" == self.build_source(
+                'Welcome, @username!')
+        assert """\
+w('\\n<i>\\n')
+
+w(username); w('\\n</i>')""" == self.build_source("""
+<i>
+    @username
+</i>""")
+
+    def test_if(self):
+        """ Test if elif else statements.
+        """
+        assert """\
+if n > 0:
+    w('    Positive\\n')
+elif n == 0:
+    w('    Zero\\n')
+else:
+    w('    Negative\\n')""" == self.build_source("""\
+@if n > 0:
+    Positive
+@elif n == 0:
+    Zero
+@else:
+    Negative
+@end
+""")
+
+    def test_def(self):
+        """ Test def statement.
+        """
+        assert """\
+def link(url, text):
+    _b = []; w = _b.append; w('        <a href="'); w(url); \
+w('">'); w(text); w('</a>\\n'); return ''.join(_b)
+local_defs['link'] = link
+w('    Please '); w(link('/en/signin', 'sign in')); w('.\\n')\
+""" == self.build_source("""\
+    @def link(url, text):
+        <a href="@url">@text</a>
+    @end
+    Please @link('/en/signin', 'sign in').
+""")
+
+    def test_render(self):
+        """ Test build_render.
+        """
+        assert """\
+def render(ctx, local_defs, super_defs):
+    _b = []; w = _b.append
+    w('Hello')
+    return ''.join(_b)""" == self.build_render("Hello")
+
+    def test_extends(self):
+        """ Test build_extends.
+        """
+        assert """\
+def render(ctx, local_defs, super_defs):
+    _b = []; w = _b.append
+    w('Hello')
+    return includes["base.html"](ctx, local_defs, super_defs)\
+""" == self.build_extends('"base.html"', 'Hello')