Patchwork [5,of,8] revset: extract parsing logic out of formatspec

login
register
mail settings
Submitter Boris Feld
Date Jan. 11, 2019, 11:29 a.m.
Message ID <d360e33ec21c012b8990.1547206147@Laptop-Boris.lan>
Download mbox | patch
Permalink /patch/37662/
State Accepted
Headers show

Comments

Boris Feld - Jan. 11, 2019, 11:29 a.m.
# HG changeset patch
# User Boris Feld <boris.feld@octobus.net>
# Date 1546565344 -3600
#      Fri Jan 04 02:29:04 2019 +0100
# Node ID d360e33ec21c012b8990dfb1148b4fd52f0ece31
# Parent  38733dd85595782676175141111a42f253efabb6
# EXP-Topic revs-efficiency
# Available At https://bitbucket.org/octobus/mercurial-devel/
#              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r d360e33ec21c
revset: extract parsing logic out of formatspec

We want to be able to perform better handling of some input when running
revset (eg: `repo.revs("%ld", somerevs)`). The first step is to be able to
access some of the parsed content before it gets substituted. There are many
possible different substitutions, we'll add support for them gradually.

In this changeset we support none, we just split some logic in a sub function
as a preparatory step.
Yuya Nishihara - Jan. 12, 2019, 5:01 a.m.
On Fri, 11 Jan 2019 12:29:07 +0100, Boris Feld wrote:
> # HG changeset patch
> # User Boris Feld <boris.feld@octobus.net>
> # Date 1546565344 -3600
> #      Fri Jan 04 02:29:04 2019 +0100
> # Node ID d360e33ec21c012b8990dfb1148b4fd52f0ece31
> # Parent  38733dd85595782676175141111a42f253efabb6
> # EXP-Topic revs-efficiency
> # Available At https://bitbucket.org/octobus/mercurial-devel/
> #              hg pull https://bitbucket.org/octobus/mercurial-devel/ -r d360e33ec21c
> revset: extract parsing logic out of formatspec

> --- a/mercurial/revsetlang.py
> +++ b/mercurial/revsetlang.py
> @@ -666,6 +666,21 @@ def formatspec(expr, *args):
>      >>> formatspec(b'%ls', [b'a', b"'"])
>      "_list('a\\\\x00\\\\'')"
>      '''
> +    parsed = _parseargs(expr, args)
> +    ret = []
> +    for t, arg in parsed:
> +        if t is None:
> +            ret.append(arg)

raise ProgrammingError otherwise.

> +    return b''.join(ret)

Patch

diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -666,6 +666,21 @@  def formatspec(expr, *args):
     >>> formatspec(b'%ls', [b'a', b"'"])
     "_list('a\\\\x00\\\\'')"
     '''
+    parsed = _parseargs(expr, args)
+    ret = []
+    for t, arg in parsed:
+        if t is None:
+            ret.append(arg)
+    return b''.join(ret)
+
+def _parseargs(expr, args):
+    """parse the expression and replace all inexpensive args
+
+    return a list of tuple [(arg-type, arg-value)]
+
+    Arg-type can be:
+    * None: a string ready to be concatenated into a final spec
+    """
     expr = pycompat.bytestr(expr)
     argiter = iter(args)
     ret = []
@@ -673,16 +688,16 @@  def formatspec(expr, *args):
     while pos < len(expr):
         q = expr.find('%', pos)
         if q < 0:
-            ret.append(expr[pos:])
+            ret.append((None, expr[pos:]))
             break
-        ret.append(expr[pos:q])
+        ret.append((None, expr[pos:q]))
         pos = q + 1
         try:
             d = expr[pos]
         except IndexError:
             raise error.ParseError(_('incomplete revspec format character'))
         if d == '%':
-            ret.append(d)
+            ret.append((None, d))
             pos += 1
             continue
 
@@ -692,19 +707,20 @@  def formatspec(expr, *args):
             raise error.ParseError(_('missing argument for revspec'))
         f = _formatlistfuncs.get(d)
         if f:
-            # a list of some type
+            # a list of some type, might be expensive, do not replace
             pos += 1
             try:
                 d = expr[pos]
             except IndexError:
                 raise error.ParseError(_('incomplete revspec format character'))
             try:
-                ret.append(f(list(arg), d))
+                ret.append((None, f(list(arg), d)))
             except (TypeError, ValueError):
                 raise error.ParseError(_('invalid argument for revspec'))
         else:
+            # a single entry, not expensive, replace
             try:
-                ret.append(_formatargtype(d, arg))
+                ret.append((None, _formatargtype(d, arg)))
             except (TypeError, ValueError):
                 raise error.ParseError(_('invalid argument for revspec'))
         pos += 1
@@ -714,7 +730,7 @@  def formatspec(expr, *args):
         raise error.ParseError(_('too many revspec arguments specified'))
     except StopIteration:
         pass
-    return ''.join(ret)
+    return ret
 
 def prettyformat(tree):
     return parser.prettyformat(tree, ('string', 'symbol'))