1import os
2import sys
3
4from pyjade import Parser, Compiler as _Compiler
5from pyjade.runtime import attrs as _attrs
6from pyjade.utils import process
7ATTRS_FUNC = '__pyjade_attrs'
8ITER_FUNC = '__pyjade_iter'
9
10def attrs(attrs, terse=False):
11    return _attrs(attrs, terse, MakoUndefined)
12
13class Compiler(_Compiler):
14    useRuntime = True
15    def compile_top(self):
16        return '# -*- coding: utf-8 -*-\n<%%! from pyjade.runtime import attrs as %s, iteration as %s\nfrom mako.runtime import Undefined %%>' % (ATTRS_FUNC,ITER_FUNC)
17
18    def interpolate(self, text, escape=True):
19        return self._interpolate(text,lambda x:'${%s}'%x)
20
21    def visitCodeBlock(self,block):
22        if self.mixing > 0:
23          self.buffer('${caller.body() if caller else ""}')
24        else:
25          self.buffer('<%%block name="%s">'%block.name)
26          if block.mode=='append': self.buffer('${parent.%s()}'%block.name)
27          self.visitBlock(block)
28          if block.mode=='prepend': self.buffer('${parent.%s()}'%block.name)
29          self.buffer('</%block>')
30
31    def visitMixin(self,mixin):
32        self.mixing += 1
33        if not mixin.call:
34          self.buffer('<%%def name="%s(%s)">'%(mixin.name,mixin.args))
35          self.visitBlock(mixin.block)
36          self.buffer('</%def>')
37        elif mixin.block:
38          self.buffer('<%%call expr="%s(%s)">'%(mixin.name,mixin.args))
39          self.visitBlock(mixin.block)
40          self.buffer('</%call>')
41        else:
42          self.buffer('${%s(%s)}'%(mixin.name,mixin.args))
43        self.mixing -= 1
44
45    def visitAssignment(self,assignment):
46        self.buffer('<%% %s = %s %%>'%(assignment.name,assignment.val))
47
48    def visitExtends(self,node):
49        path = self.format_path(node.path)
50        self.buffer('<%%inherit file="%s"/>'%(path))
51
52    def visitInclude(self,node):
53        path = self.format_path(node.path)
54        self.buffer('<%%include file="%s"/>'%(path))
55        self.buffer('<%%namespace file="%s" import="*"/>'%(path))
56
57
58    def visitConditional(self,conditional):
59        TYPE_CODE = {
60            'if': lambda x: 'if %s'%x,
61            'unless': lambda x: 'if not %s'%x,
62            'elif': lambda x: 'elif %s'%x,
63            'else': lambda x: 'else'
64        }
65        self.buf.append('\\\n%% %s:\n'%TYPE_CODE[conditional.type](conditional.sentence))
66        if conditional.block:
67            self.visit(conditional.block)
68            for next in conditional.next:
69              self.visitConditional(next)
70        if conditional.type in ['if','unless']: self.buf.append('\\\n% endif\n')
71
72
73    def visitVar(self,var,escape=False):
74        return '${%s%s}'%(var,'| h' if escape else '| n')
75
76    def visitCode(self,code):
77        if code.buffer:
78            val = code.val.lstrip()
79            val = self.var_processor(val)
80            self.buf.append(self.visitVar(val, code.escape))
81        else:
82            self.buf.append('<%% %s %%>'%code.val)
83
84        if code.block:
85            # if not code.buffer: self.buf.append('{')
86            self.visit(code.block)
87            # if not code.buffer: self.buf.append('}')
88
89            if not code.buffer:
90              codeTag = code.val.strip().split(' ',1)[0]
91              if codeTag in self.autocloseCode:
92                  self.buf.append('</%%%s>'%codeTag)
93
94    def visitEach(self,each):
95        self.buf.append('\\\n%% for %s in %s(%s,%d):\n'%(','.join(each.keys),ITER_FUNC,each.obj,len(each.keys)))
96        self.visit(each.block)
97        self.buf.append('\\\n% endfor\n')
98
99    def attributes(self,attrs):
100        return "${%s(%s, undefined=Undefined) | n}"%(ATTRS_FUNC,attrs)
101
102
103
104def preprocessor(source):
105    return process(source,compiler=Compiler)
106