[PATCH] revset: add experimental set subscript operator
Yuya Nishihara
yuya at tcha.org
Fri Jun 30 14:58:43 UTC 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