Patchwork [4,of,5] templater: add function to parse whole string as template expression

login
register
mail settings
Submitter Yuya Nishihara
Date April 14, 2016, 3:42 p.m.
Message ID <6e60bc44a476c2279254.1460648573@mimosa>
Download mbox | patch
Permalink /patch/14624/
State Accepted
Headers show

Comments

Yuya Nishihara - April 14, 2016, 3:42 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1459078143 -32400
#      Sun Mar 27 20:29:03 2016 +0900
# Node ID 6e60bc44a476c227925491d6cf3ac1571016574e
# Parent  f74158011bce3ffaaf3c33399ff1b3e728d5fe66
templater: add function to parse whole string as template expression

This will be a parser of template aliases, and it can also be used for
processing quoted string templates in map files. That's why this function
isn't defined in the upcoming _aliasrules class.

Patch

diff --git a/mercurial/templater.py b/mercurial/templater.py
--- a/mercurial/templater.py
+++ b/mercurial/templater.py
@@ -40,7 +40,9 @@  elements = {
     "end": (0, None, None, None, None),
 }
 
-def tokenize(program, start, end):
+def tokenize(program, start, end, term=None):
+    """Parse a template expression into a stream of tokens, which must end
+    with term if specified"""
     pos = start
     while pos < end:
         c = program[pos]
@@ -127,13 +129,15 @@  def tokenize(program, start, end):
             sym = program[s:pos]
             yield ('symbol', sym, s)
             pos -= 1
-        elif c == '}':
+        elif c == term:
             yield ('end', None, pos + 1)
             return
         else:
             raise error.ParseError(_("syntax error"), pos)
         pos += 1
-    raise error.ParseError(_("unterminated template expansion"), start)
+    if term:
+        raise error.ParseError(_("unterminated template expansion"), start)
+    yield ('end', None, pos)
 
 def _parsetemplate(tmpl, start, stop, quote=''):
     r"""
@@ -171,7 +175,7 @@  def _parsetemplate(tmpl, start, stop, qu
         if c == quote:
             return parsed, n + 1
 
-        parseres, pos = p.parse(tokenize(tmpl, n + 1, stop))
+        parseres, pos = p.parse(tokenize(tmpl, n + 1, stop, '}'))
         parsed.append(parseres)
 
     if quote:
@@ -218,6 +222,28 @@  def parse(tmpl):
     assert pos == len(tmpl), 'unquoted template should be consumed'
     return _unnesttemplatelist(('template', parsed))
 
+def _parseexpr(expr):
+    """Parse a template expression into tree
+
+    >>> _parseexpr('"foo"')
+    ('string', 'foo')
+    >>> _parseexpr('foo(bar)')
+    ('func', ('symbol', 'foo'), ('symbol', 'bar'))
+    >>> _parseexpr('foo(')
+    Traceback (most recent call last):
+      ...
+    ParseError: ('not a prefix: end', 4)
+    >>> _parseexpr('"foo" "bar"')
+    Traceback (most recent call last):
+      ...
+    ParseError: ('invalid token', 7)
+    """
+    p = parser.parser(elements)
+    tree, pos = p.parse(tokenize(expr, 0, len(expr)))
+    if pos != len(expr):
+        raise error.ParseError(_('invalid token'), pos)
+    return _unnesttemplatelist(tree)
+
 def prettyformat(tree):
     return parser.prettyformat(tree, ('integer', 'string', 'symbol'))