[PATCH] revset: add experimental set subscript operator

Yuya Nishihara yuya at tcha.org
Fri Jun 30 10:58:43 EDT 2017


# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1498302078 -32400
#      Sat Jun 24 20:01:18 2017 +0900
# Node ID b7f6740b0e58c468ecc82296fe1ee0800a7e0582
# Parent  fe3419b41a456661a3d35ae963d06a203d4446a2
revset: add experimental set subscript operator

It only supports integer indices as a beginning. See the following post
for further ideas.

https://www.mercurial-scm.org/wiki/RevsetOperatorPlan#ideas_from_mpm

I'm not sure if the current semantics are the most useful and extendable.
We might want the way to select the nth ancestors/descendants in sub graph,
by e.g. 'follow(filename){-2}', which is completely different so the '{n}'
operator wouldn't be usable.

diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -21,6 +21,7 @@ from . import (
 elements = {
     # token-type: binding-strength, primary, prefix, infix, suffix
     "(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
+    "{": (21, None, None, ("subscript", 1, "}"), None),
     "##": (20, None, None, ("_concat", 20), None),
     "~": (18, None, None, ("ancestor", 18), None),
     "^": (18, None, None, ("parent", 18), "parentpost"),
@@ -39,6 +40,7 @@ elements = {
     "=": (3, None, None, ("keyvalue", 3), None),
     ",": (2, None, None, ("list", 2), None),
     ")": (0, None, None, None, None),
+    "}": (0, None, None, None, None),
     "symbol": (0, "symbol", None, None, None),
     "string": (0, "string", None, None, None),
     "end": (0, None, None, None, None),
@@ -47,7 +49,7 @@ elements = {
 keywords = {'and', 'or', 'not'}
 
 _quoteletters = {'"', "'"}
-_simpleopletters = set(pycompat.iterbytestr("():=,-|&+!~^%"))
+_simpleopletters = set(pycompat.iterbytestr("(){}:=,-|&+!~^%"))
 
 # default set of valid characters for the initial letter of symbols
 _syminitletters = set(pycompat.iterbytestr(
@@ -310,6 +312,18 @@ def _matchonly(revs, bases):
     if _isposargs(ta, 1) and _isposargs(tb, 1):
         return ('list', ta, tb)
 
+def _transformsubscript(x, y):
+    """Rewrite 'x{y}' operator to evaluatable tree"""
+    # this is pretty basic implementation of 'x{y}' operator, still
+    # experimental so undocumented. see the wiki for further ideas.
+    # https://www.mercurial-scm.org/wiki/RevsetOperatorPlan#ideas_from_mpm
+    n = getinteger(y, _("set subscript must be an integer"))
+    if n <= 0:
+        y = ('symbol', '%s' % -n)
+        return ('func', ('symbol', 'ancestors'), ('list', x, y, y))
+    else:
+        return ('func', ('symbol', 'descendants'), ('list', x, y, y))
+
 def _fixops(x):
     """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
     handled well by our simple top-down parser"""
@@ -377,6 +391,9 @@ def _analyze(x, order):
         return (op,) + tuple(_analyze(y, order) for y in x[1:])
     elif op == 'keyvalue':
         return (op, x[1], _analyze(x[2], order))
+    elif op == 'subscript':
+        t = _transformsubscript(x[1], _analyze(x[2], order))
+        return _analyze(t, order)
     elif op == 'func':
         f = getsymbol(x[1])
         d = defineorder
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -499,6 +499,145 @@ keyword arguments
   hg: parse error: can't use a key-value pair in this context
   [255]
 
+subscript operator:
+
+  $ hg debugrevspec -p parsed -p analyzed 'tip{0}'
+  * parsed:
+  (subscript
+    ('symbol', 'tip')
+    ('symbol', '0'))
+  * analyzed:
+  (func
+    ('symbol', 'ancestors')
+    (list
+      ('symbol', 'tip')
+      ('symbol', '0')
+      ('symbol', '0'))
+    define)
+  9
+
+  $ hg debugrevspec -p parsed -p analyzed '.{-1}'
+  * parsed:
+  (subscript
+    ('symbol', '.')
+    (negate
+      ('symbol', '1')))
+  * analyzed:
+  (func
+    ('symbol', 'ancestors')
+    (list
+      ('symbol', '.')
+      ('symbol', '1')
+      ('symbol', '1'))
+    define)
+  8
+
+  $ hg debugrevspec -p parsed -p analyzed 'roots(:){2}'
+  * parsed:
+  (subscript
+    (func
+      ('symbol', 'roots')
+      (rangeall
+        None))
+    ('symbol', '2'))
+  * analyzed:
+  (func
+    ('symbol', 'descendants')
+    (list
+      (func
+        ('symbol', 'roots')
+        (rangeall
+          None
+          define)
+        define)
+      ('symbol', '2')
+      ('symbol', '2'))
+    define)
+  2
+  3
+
+ has the highest binding strength (as function call)
+
+  $ hg debugrevspec -p parsed 'tip:tip^{-1}'
+  * parsed:
+  (range
+    ('symbol', 'tip')
+    (subscript
+      (parentpost
+        ('symbol', 'tip'))
+      (negate
+        ('symbol', '1'))))
+  9
+  8
+  7
+  6
+  5
+  4
+
+  $ hg debugrevspec -p parsed --no-show-revs 'not public(){0}'
+  * parsed:
+  (not
+    (subscript
+      (func
+        ('symbol', 'public')
+        None)
+      ('symbol', '0')))
+
+ left-hand side should be optimized recursively
+
+  $ hg debugrevspec -p parsed -p analyzed -p optimized --no-show-revs \
+  > '(not public()){0}'
+  * parsed:
+  (subscript
+    (group
+      (not
+        (func
+          ('symbol', 'public')
+          None)))
+    ('symbol', '0'))
+  * analyzed:
+  (func
+    ('symbol', 'ancestors')
+    (list
+      (not
+        (func
+          ('symbol', 'public')
+          None
+          any)
+        define)
+      ('symbol', '0')
+      ('symbol', '0'))
+    define)
+  * optimized:
+  (func
+    ('symbol', 'ancestors')
+    (list
+      (func
+        ('symbol', '_notpublic')
+        None
+        any)
+      ('symbol', '0')
+      ('symbol', '0'))
+    define)
+
+ parse errors
+
+  $ hg debugrevspec '{0}'
+  hg: parse error at 0: not a prefix: {
+  [255]
+  $ hg debugrevspec '.{0'
+  hg: parse error at 3: unexpected token: end
+  [255]
+  $ hg debugrevspec '.}'
+  hg: parse error at 1: invalid token
+  [255]
+  $ hg debugrevspec '.{a}'
+  hg: parse error: set subscript must be an integer
+  [255]
+  $ hg debugrevspec '.{1-2}'
+  hg: parse error: set subscript must be an integer
+  [255]
+
 parsed tree at stages:
 
   $ hg debugrevspec -p all '()'


More information about the Mercurial-devel mailing list