# HG changeset patch # User Yuya Nishihara # Date 1455375264 -32400 # Node ID 1d461ee26e1bf536daa7b320e6e485005ad8a37a # Parent 4d93d73b8aecb2436bbfe9248cdfe0ef21582baf templater: lift parsed and compiled templates to generic data types Before this patch, parsed and compiled templates were kept as lists. That was inconvenient for applying transformation such as alias expansion. This patch changes the types of the outermost objects as follows: stage old new -------- -------------- ------------------------------ parsed [(op, ..)] ('template', [(op, ..)]) compiled [(func, data)] (runtemplate, [(func, data)]) New templater.parse() function has the same signature as revset.parse() and fileset.parse(). diff -r 4d93d73b8aec -r 1d461ee26e1b mercurial/templater.py --- a/mercurial/templater.py Tue Mar 15 15:50:57 2016 -0700 +++ b/mercurial/templater.py Sat Feb 13 23:54:24 2016 +0900 @@ -177,9 +177,15 @@ raise error.ParseError(_("unterminated string"), start) return parsed, pos -def compiletemplate(tmpl, context): +def parse(tmpl): + """Parse template string into tree""" parsed, pos = _parsetemplate(tmpl, 0, len(tmpl)) - return [compileexp(e, context, methods) for e in parsed] + assert pos == len(tmpl), 'unquoted template should be consumed' + return ('template', parsed) + +def compiletemplate(tmpl, context): + """Parse and compile template string to (func, data) pair""" + return compileexp(parse(tmpl), context, methods) def compileexp(exp, context, curmethods): t = exp[0] @@ -202,8 +208,10 @@ return [x] def gettemplate(exp, context): + """Compile given template tree or load named template from map file; + returns (func, data) pair""" if exp[0] == 'template': - return [compileexp(e, context, methods) for e in exp[1]] + return compileexp(exp, context, methods) if exp[0] == 'symbol': # unlike runsymbol(), here 'symbol' is always taken as template name # even if it exists in mapping. this allows us to override mapping @@ -308,11 +316,11 @@ def buildmap(exp, context): func, data = compileexp(exp[1], context, methods) - ctmpl = gettemplate(exp[2], context) - return (runmap, (func, data, ctmpl)) + tfunc, tdata = gettemplate(exp[2], context) + return (runmap, (func, data, tfunc, tdata)) def runmap(context, mapping, data): - func, data, ctmpl = data + func, data, tfunc, tdata = data d = func(context, mapping, data) if util.safehasattr(d, 'itermaps'): diter = d.itermaps() @@ -330,7 +338,7 @@ if isinstance(i, dict): lm.update(i) lm['originalnode'] = mapping.get('node') - yield runtemplate(context, lm, ctmpl) + yield tfunc(context, lm, tdata) else: # v is not an iterable of dicts, this happen when 'key' # has been fully expanded already and format is useless. @@ -857,13 +865,13 @@ if defaults is None: defaults = {} self._defaults = defaults - self._cache = {} + self._cache = {} # key: (func, data) def _load(self, t): '''load, parse, and cache a template''' if t not in self._cache: # put poison to cut recursion while compiling 't' - self._cache[t] = [(_runrecursivesymbol, t)] + self._cache[t] = (_runrecursivesymbol, t) try: self._cache[t] = compiletemplate(self._loader(t), self) except: # re-raises @@ -875,7 +883,8 @@ '''Perform expansion. t is name of map element to expand. mapping contains added elements for use during expansion. Is a generator.''' - return _flatten(runtemplate(self, mapping, self._load(t))) + func, data = self._load(t) + return _flatten(func(self, mapping, data)) engines = {'default': engine}