[PATCH 2 of 3] revset: add experimental relation and subscript operators
Yuya Nishihara
yuya at tcha.org
Thu Jul 13 12:16:18 EDT 2017
# HG changeset patch
# User Yuya Nishihara <yuya at tcha.org>
# Date 1499486879 -32400
# Sat Jul 08 13:07:59 2017 +0900
# Node ID eed6cd6aa792698d0535f276e150f88500943866
# Parent f98c5d417af06e54df22a58fa2e6305ff7b5a7ba
revset: add experimental relation and subscript operators
The proposed syntax [1] was originally 'set{n rel}', but it seemed slightly
confusing if template is involved. On the other hand, we want to keep 'set[n]'
for future extension. So this patch introduces 'set#rel[n]' ternary operator.
I chose '#' just because it looks like applying an attribute.
This also adds stubs for 'set[n]' and 'set#rel' operators since these syntax
elements are fundamental for constructing 'set#rel[n]'.
[1]: https://www.mercurial-scm.org/wiki/RevsetOperatorPlan#ideas_from_mpm
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -151,6 +151,15 @@ def orset(repo, subset, x, order):
def notset(repo, subset, x, order):
return subset - getset(repo, subset, x)
+def relationset(repo, subset, x, y, order):
+ raise error.ParseError(_("can't use a relation in this context"))
+
+def relsubscriptset(repo, subset, x, y, z, order):
+ raise error.ParseError(_("can't use a relation in this context"))
+
+def subscriptset(repo, subset, x, y, order):
+ raise error.ParseError(_("can't use a subscript in this context"))
+
def listset(repo, subset, *xs):
raise error.ParseError(_("can't use a list in this context"),
hint=_('see hg help "revsets.x or y"'))
@@ -2004,6 +2013,9 @@ methods = {
"or": orset,
"not": notset,
"difference": differenceset,
+ "relation": relationset,
+ "relsubscript": relsubscriptset,
+ "subscript": subscriptset,
"list": listset,
"keyvalue": keyvaluepair,
"func": func,
diff --git a/mercurial/revsetlang.py b/mercurial/revsetlang.py
--- a/mercurial/revsetlang.py
+++ b/mercurial/revsetlang.py
@@ -21,6 +21,8 @@ from . import (
elements = {
# token-type: binding-strength, primary, prefix, infix, suffix
"(": (21, None, ("group", 1, ")"), ("func", 1, ")"), None),
+ "[": (21, None, None, ("subscript", 1, "]"), None),
+ "#": (21, None, None, ("relation", 21), None),
"##": (20, None, None, ("_concat", 20), None),
"~": (18, None, None, ("ancestor", 18), None),
"^": (18, None, None, ("parent", 18), "parentpost"),
@@ -39,6 +41,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 +50,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(
@@ -331,6 +334,9 @@ def _fixops(x):
# make number of arguments deterministic:
# x + y + z -> (or x y z) -> (or (list x y z))
return (op, _fixops(('list',) + x[1:]))
+ elif op == 'subscript' and x[1][0] == 'relation':
+ # x#y[z] ternary
+ return _fixops(('relsubscript', x[1][1], x[1][2], x[2]))
return (op,) + tuple(_fixops(y) for y in x[1:])
@@ -369,10 +375,16 @@ def _analyze(x, order):
return (op, _analyze(x[1], defineorder), order)
elif op == 'group':
return _analyze(x[1], order)
- elif op in ('dagrange', 'range', 'parent', 'ancestor'):
+ elif op in ('dagrange', 'range', 'parent', 'ancestor', 'relation',
+ 'subscript'):
ta = _analyze(x[1], defineorder)
tb = _analyze(x[2], defineorder)
return (op, ta, tb, order)
+ elif op == 'relsubscript':
+ ta = _analyze(x[1], defineorder)
+ tb = _analyze(x[2], defineorder)
+ tc = _analyze(x[3], defineorder)
+ return (op, ta, tb, tc, order)
elif op == 'list':
return (op,) + tuple(_analyze(y, order) for y in x[1:])
elif op == 'keyvalue':
@@ -481,10 +493,14 @@ def _optimize(x, small):
wb, tb = _optimize(x[2], small)
order = x[3]
return wa + wb, (op, ta, tb, order)
- elif op in ('parent', 'ancestor'):
+ elif op in ('parent', 'ancestor', 'relation', 'subscript'):
w, t = _optimize(x[1], small)
order = x[3]
return w, (op, t, x[2], order)
+ elif op == 'relsubscript':
+ w, t = _optimize(x[1], small)
+ order = x[4]
+ return w, (op, t, x[2], x[3], order)
elif op == 'list':
ws, ts = zip(*(_optimize(y, small) for y in x[1:]))
return sum(ws), (op,) + ts
diff --git a/tests/test-annotate.t b/tests/test-annotate.t
--- a/tests/test-annotate.t
+++ b/tests/test-annotate.t
@@ -812,7 +812,7 @@ check error cases
abort: line range exceeds file size
[255]
$ hg log -r 'followlines(baz, 2:4, startrev=20, descend=[1])'
- hg: parse error at 43: syntax error in revset 'followlines(baz, 2:4, startrev=20, descend=[1])'
+ hg: parse error at 43: not a prefix: [
[255]
$ hg log -r 'followlines(baz, 2:4, startrev=20, descend=a)'
hg: parse error: descend argument must be a boolean
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -500,6 +500,151 @@ keyword arguments
hg: parse error: can't use a key-value pair in this context
[255]
+relation-subscript operator has the highest binding strength (as function call):
+
+ $ hg debugrevspec -p parsed 'tip:tip^#generations[-1]'
+ * parsed:
+ (range
+ ('symbol', 'tip')
+ (relsubscript
+ (parentpost
+ ('symbol', 'tip'))
+ ('symbol', 'generations')
+ (negate
+ ('symbol', '1'))))
+ hg: parse error: can't use a relation in this context
+ [255]
+
+ $ hg debugrevspec -p parsed --no-show-revs 'not public()#generations[0]'
+ * parsed:
+ (not
+ (relsubscript
+ (func
+ ('symbol', 'public')
+ None)
+ ('symbol', 'generations')
+ ('symbol', '0')))
+ hg: parse error: can't use a relation in this context
+ [255]
+
+left-hand side of relation-subscript operator should be optimized recursively:
+
+ $ hg debugrevspec -p analyzed -p optimized --no-show-revs \
+ > '(not public())#generations[0]'
+ * analyzed:
+ (relsubscript
+ (not
+ (func
+ ('symbol', 'public')
+ None
+ any)
+ define)
+ ('symbol', 'generations')
+ ('symbol', '0')
+ define)
+ * optimized:
+ (relsubscript
+ (func
+ ('symbol', '_notpublic')
+ None
+ any)
+ ('symbol', 'generations')
+ ('symbol', '0')
+ define)
+ hg: parse error: can't use a relation in this context
+ [255]
+
+resolution of subscript and relation-subscript ternary operators:
+
+ $ hg debugrevspec -p analyzed 'tip[0]'
+ * analyzed:
+ (subscript
+ ('symbol', 'tip')
+ ('symbol', '0')
+ define)
+ hg: parse error: can't use a subscript in this context
+ [255]
+
+ $ hg debugrevspec -p analyzed 'tip#rel[0]'
+ * analyzed:
+ (relsubscript
+ ('symbol', 'tip')
+ ('symbol', 'rel')
+ ('symbol', '0')
+ define)
+ hg: parse error: can't use a relation in this context
+ [255]
+
+ $ hg debugrevspec -p analyzed '(tip#rel)[0]'
+ * analyzed:
+ (subscript
+ (relation
+ ('symbol', 'tip')
+ ('symbol', 'rel')
+ define)
+ ('symbol', '0')
+ define)
+ hg: parse error: can't use a subscript in this context
+ [255]
+
+ $ hg debugrevspec -p analyzed 'tip#rel[0][1]'
+ * analyzed:
+ (subscript
+ (relsubscript
+ ('symbol', 'tip')
+ ('symbol', 'rel')
+ ('symbol', '0')
+ define)
+ ('symbol', '1')
+ define)
+ hg: parse error: can't use a subscript in this context
+ [255]
+
+ $ hg debugrevspec -p analyzed 'tip#rel0#rel1[1]'
+ * analyzed:
+ (relsubscript
+ (relation
+ ('symbol', 'tip')
+ ('symbol', 'rel0')
+ define)
+ ('symbol', 'rel1')
+ ('symbol', '1')
+ define)
+ hg: parse error: can't use a relation in this context
+ [255]
+
+ $ hg debugrevspec -p analyzed 'tip#rel0[0]#rel1[1]'
+ * analyzed:
+ (relsubscript
+ (relsubscript
+ ('symbol', 'tip')
+ ('symbol', 'rel0')
+ ('symbol', '0')
+ define)
+ ('symbol', 'rel1')
+ ('symbol', '1')
+ define)
+ hg: parse error: can't use a relation in this context
+ [255]
+
+parse errors of relation, subscript and relation-subscript operators:
+
+ $ hg debugrevspec '[0]'
+ hg: parse error at 0: not a prefix: [
+ [255]
+ $ hg debugrevspec '.#'
+ hg: parse error at 2: not a prefix: end
+ [255]
+ $ hg debugrevspec '#rel'
+ hg: parse error at 0: not a prefix: #
+ [255]
+ $ hg debugrevspec '.#rel[0'
+ hg: parse error at 7: unexpected token: end
+ [255]
+ $ hg debugrevspec '.]'
+ hg: parse error at 1: invalid token
+ [255]
+
parsed tree at stages:
$ hg debugrevspec -p all '()'
More information about the Mercurial-devel
mailing list