templater: lift parsed and compiled templates to generic data types
authorYuya Nishihara <yuya@tcha.org>
Sat, 13 Feb 2016 23:54:24 +0900
changeset 28545 1d461ee26e1b
parent 28544 4d93d73b8aec
child 28546 1987ed32efca
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().
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}