templater: expand list of parsed templates to template node
authorYuya Nishihara <yuya@tcha.org>
Sat, 13 Feb 2016 23:20:47 +0900
changeset 28547 73d01cba5810
parent 28546 1987ed32efca
child 28548 b7a31068cc80
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.
mercurial/templater.py
--- a/mercurial/templater.py	Sun Feb 14 15:42:49 2016 +0900
+++ b/mercurial/templater.py	Sat Feb 13 23:20:47 2016 +0900
@@ -177,11 +177,48 @@
         raise error.ParseError(_("unterminated string"), start)
     return parsed, pos
 
+def _unnesttemplatelist(tree):
+    """Expand list of templates to node tuple
+
+    >>> def f(tree):
+    ...     print prettyformat(_unnesttemplatelist(tree))
+    >>> f(('template', []))
+    ('string', '')
+    >>> f(('template', [('string', 'foo')]))
+    ('string', 'foo')
+    >>> f(('template', [('string', 'foo'), ('symbol', 'rev')]))
+    (template
+      ('string', 'foo')
+      ('symbol', 'rev'))
+    >>> f(('template', [('symbol', 'rev')]))  # template(rev) -> str
+    (template
+      ('symbol', 'rev'))
+    >>> f(('template', [('template', [('string', 'foo')])]))
+    ('string', 'foo')
+    """
+    if not isinstance(tree, tuple):
+        return tree
+    op = tree[0]
+    if op != 'template':
+        return (op,) + tuple(_unnesttemplatelist(x) for x in tree[1:])
+
+    assert len(tree) == 2
+    xs = tuple(_unnesttemplatelist(x) for x in tree[1])
+    if not xs:
+        return ('string', '')  # empty template ""
+    elif len(xs) == 1 and xs[0][0] == 'string':
+        return xs[0]  # fast path for string with no template fragment "x"
+    else:
+        return (op,) + xs
+
 def parse(tmpl):
     """Parse template string into tree"""
     parsed, pos = _parsetemplate(tmpl, 0, len(tmpl))
     assert pos == len(tmpl), 'unquoted template should be consumed'
-    return ('template', parsed)
+    return _unnesttemplatelist(('template', parsed))
+
+def prettyformat(tree):
+    return parser.prettyformat(tree, ('integer', 'string', 'symbol'))
 
 def compiletemplate(tmpl, context):
     """Parse and compile template string to (func, data) pair"""
@@ -281,9 +318,7 @@
     return v
 
 def buildtemplate(exp, context):
-    ctmpl = [compileexp(e, context, methods) for e in exp[1]]
-    if len(ctmpl) == 1:
-        return ctmpl[0]  # fast path for string with no template fragment
+    ctmpl = [compileexp(e, context, methods) for e in exp[1:]]
     return (runtemplate, ctmpl)
 
 def runtemplate(context, mapping, template):