mako的生成模板首先需要把文本编译成可执行的Python代码,然后再从外部添加变量,进行执行,输出文本
编译过程也要分为两部分,先是根据文档结构进行解析,然后根据解析好的节点生成Python代码(好像编译原理中的中间代码生成啊,后悔上课不认真听那门课T^T)
这边文章我们先看解析成节点
老规矩,先上测试案例
def test_integration(self):
template = """<%namespace name="foo" file="somefile.html"/>
# inherit from foobar.html
<%inherit file="foobar.html"/>
<%component name="header">
<div>header</div>
</%component>
<%component name="footer">
<div> footer</div>
</%component>
<table>
% for j in data():
<tr>
% for x in j:
<td>Hello ${x| h}</td>
% endfor
</tr>
% endfor
</table>
"""
nodes = Lexer(template).parse()
#print nodes
assert repr(nodes) == r"""TemplateNode({}, [NamespaceTag('namespace', {'name': '"foo"', 'file': '"somefile.html"'}, (1, 1), []), Text('\n', (1, 46)), Comment('inherit from foobar.html', (2, 1)), InheritTag('inherit', {'file': '"foobar.html"'}, (3, 1), []), Text('\n\n', (3, 31)), ComponentTag('component', {'name': '"header"'}, (5, 1), ["Text('\\n <div>header</div>\\n', (5, 27))"]), Text('\n', (7, 14)), ComponentTag('component', {'name': '"footer"'}, (8, 1), ["Text('\\n <div> footer</div>\\n', (8, 27))"]), Text('\n\n<table>\n', (10, 14)), ControlLine('for', 'for j in data():', False, (13, 1)), Text(' <tr>\n', (14, 1)), ControlLine('for', 'for x in j:', False, (15, 1)), Text(' <td>Hello ', (16, 1)), Expression('x', ['h'], (16, 23)), Text('</td>\n', (16, 30)), ControlLine('for', 'endfor', True, (17, 1)), Text(' </tr>\n', (18, 1)), ControlLine('for', 'endfor', True, (19, 1)), Text('</table>\n', (20, 1))])"""
注意!,这里的节点并不是指html的节点,指的是mako当中的标签,如<%block>,<%def>这种,mako是文本模板引擎,而不是html模板引擎
上面测试代码的template是一段混合着mako各种元素的文本,让我们看看mako是如何对这段文本进行操作的
上面的测试代码用到了两个方法
class Lexer(object):
def __init__(self, text):
self.text = text
self.template = parsetree.TemplateNode()
self.matched_lineno = 1
self.matched_charpos = 0
self.lineno = 1
self.match_position = 0
self.tag = []
self.control_line = []
def parse(self):
length = len(self.text)
while (True):
if self.match_position > length:
break
if self.match_end():
break
if self.match_expression():
continue
if self.match_control_line():
continue
if self.match_tag_start():
continue
if self.match_tag_end():
continue
if self.match_python_block():
continue
if self.match_text():
continue
if (self.current.