comparison mercurial/templater.py @ 28547:73d01cba5810

templater: expand list of parsed templates to template node This patch eliminates a nested data structure other than the parsed tree. ('template', [(op, data), ..]) -> ('template', (op, data), ..) New expanded tree can be processed by common parser functions. This change will help implementing template aliases. Because a (template ..) node should have at least one child node, an empty template (template []) is mapped to (string ''). Also a trivial string (template [(string ..)]) node is unwrapped to (string ..) at parsing phase, instead of compiling phase.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 13 Feb 2016 23:20:47 +0900
parents 1987ed32efca
children ed1d90f6e921
comparison
equal deleted inserted replaced
28546:1987ed32efca 28547:73d01cba5810
175 175
176 if quote: 176 if quote:
177 raise error.ParseError(_("unterminated string"), start) 177 raise error.ParseError(_("unterminated string"), start)
178 return parsed, pos 178 return parsed, pos
179 179
180 def _unnesttemplatelist(tree):
181 """Expand list of templates to node tuple
182
183 >>> def f(tree):
184 ... print prettyformat(_unnesttemplatelist(tree))
185 >>> f(('template', []))
186 ('string', '')
187 >>> f(('template', [('string', 'foo')]))
188 ('string', 'foo')
189 >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
190 (template
191 ('string', 'foo')
192 ('symbol', 'rev'))
193 >>> f(('template', [('symbol', 'rev')])) # template(rev) -> str
194 (template
195 ('symbol', 'rev'))
196 >>> f(('template', [('template', [('string', 'foo')])]))
197 ('string', 'foo')
198 """
199 if not isinstance(tree, tuple):
200 return tree
201 op = tree[0]
202 if op != 'template':
203 return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
204
205 assert len(tree) == 2
206 xs = tuple(_unnesttemplatelist(x) for x in tree[1])
207 if not xs:
208 return ('string', '') # empty template ""
209 elif len(xs) == 1 and xs[0][0] == 'string':
210 return xs[0] # fast path for string with no template fragment "x"
211 else:
212 return (op,) + xs
213
180 def parse(tmpl): 214 def parse(tmpl):
181 """Parse template string into tree""" 215 """Parse template string into tree"""
182 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl)) 216 parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
183 assert pos == len(tmpl), 'unquoted template should be consumed' 217 assert pos == len(tmpl), 'unquoted template should be consumed'
184 return ('template', parsed) 218 return _unnesttemplatelist(('template', parsed))
219
220 def prettyformat(tree):
221 return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
185 222
186 def compiletemplate(tmpl, context): 223 def compiletemplate(tmpl, context):
187 """Parse and compile template string to (func, data) pair""" 224 """Parse and compile template string to (func, data) pair"""
188 return compileexp(parse(tmpl), context, methods) 225 return compileexp(parse(tmpl), context, methods)
189 226
279 if callable(v): 316 if callable(v):
280 return v(**mapping) 317 return v(**mapping)
281 return v 318 return v
282 319
283 def buildtemplate(exp, context): 320 def buildtemplate(exp, context):
284 ctmpl = [compileexp(e, context, methods) for e in exp[1]] 321 ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
285 if len(ctmpl) == 1:
286 return ctmpl[0] # fast path for string with no template fragment
287 return (runtemplate, ctmpl) 322 return (runtemplate, ctmpl)
288 323
289 def runtemplate(context, mapping, template): 324 def runtemplate(context, mapping, template):
290 for func, data in template: 325 for func, data in template:
291 yield func(context, mapping, data) 326 yield func(context, mapping, data)