changeset 29780:abe4eecc3253

revset: resolve ambiguity of x^:y before alias expansion This is purely a parsing problem, which should be resolved before alias expansion.
author Yuya Nishihara <yuya@tcha.org>
date Sat, 06 Aug 2016 20:21:00 +0900
parents 8e4841944e68
children 9c51a5de76db
files mercurial/revset.py tests/test-revset.t
diffstat 2 files changed, 39 insertions(+), 88 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/revset.py	Sat Aug 06 19:59:28 2016 +0900
+++ b/mercurial/revset.py	Sat Aug 06 20:21:00 2016 +0900
@@ -2314,6 +2314,23 @@
         and getsymbol(bases[1][1]) == 'ancestors'):
         return ('list', revs[2], bases[1][2])
 
+def _fixops(x):
+    """Rewrite raw parsed tree to resolve ambiguous syntax which cannot be
+    handled well by our simple top-down parser"""
+    if not isinstance(x, tuple):
+        return x
+
+    op = x[0]
+    if op == 'parent':
+        # x^:y means (x^) : y, not x ^ (:y)
+        post = ('parentpost', x[1])
+        if x[2][0] == 'dagrangepre':
+            return _fixops(('dagrange', post, x[2][1]))
+        elif x[2][0] == 'rangepre':
+            return _fixops(('range', post, x[2][1]))
+
+    return (op,) + tuple(_fixops(y) for y in x[1:])
+
 def _optimize(x, small):
     if x is None:
         return 0, x
@@ -2407,14 +2424,6 @@
     elif op == 'group':
         return _optimize(x[1], small)
     elif op in 'dagrange range parent ancestorspec':
-        if op == 'parent':
-            # x^:y means (x^) : y, not x ^ (:y)
-            post = ('parentpost', x[1])
-            if x[2][0] == 'dagrangepre':
-                return _optimize(('dagrange', post, x[2][1]), small)
-            elif x[2][0] == 'rangepre':
-                return _optimize(('range', post, x[2][1]), small)
-
         wa, ta = _optimize(x[1], small)
         wb, tb = _optimize(x[2], small)
         return wa + wb, (op, ta, tb)
@@ -2470,7 +2479,7 @@
                                  syminitletters=syminitletters))
     if pos != len(spec):
         raise error.ParseError(_('invalid token'), pos)
-    return parser.simplifyinfixops(tree, ('list', 'or'))
+    return _fixops(parser.simplifyinfixops(tree, ('list', 'or')))
 
 class _aliasrules(parser.basealiasrules):
     """Parsing and expansion rule set of revset aliases"""
--- a/tests/test-revset.t	Sat Aug 06 19:59:28 2016 +0900
+++ b/tests/test-revset.t	Sat Aug 06 20:21:00 2016 +0900
@@ -482,12 +482,7 @@
 
  x^:y means (x^):y
 
-  $ try --optimize '1^:2'
-  (parent
-    ('symbol', '1')
-    (rangepre
-      ('symbol', '2')))
-  * optimized:
+  $ try '1^:2'
   (range
     (parentpost
       ('symbol', '1'))
@@ -498,12 +493,7 @@
   1
   2
 
-  $ try --optimize '1^::2'
-  (parent
-    ('symbol', '1')
-    (dagrangepre
-      ('symbol', '2')))
-  * optimized:
+  $ try '1^::2'
   (dagrange
     (parentpost
       ('symbol', '1'))
@@ -516,31 +506,18 @@
 
  x^:y should be resolved before omitting group operators
 
-  $ try --optimize '1^(:2)'
+  $ try '1^(:2)'
   (parent
     ('symbol', '1')
     (group
       (rangepre
         ('symbol', '2'))))
-  * optimized:
-  (parent
-    ('symbol', '1')
-    (range
-      ('string', '0')
-      ('symbol', '2')))
   hg: parse error: ^ expects a number 0, 1, or 2
   [255]
 
  x^:y should be resolved recursively
 
-  $ try --optimize 'sort(1^:2)'
-  (func
-    ('symbol', 'sort')
-    (parent
-      ('symbol', '1')
-      (rangepre
-        ('symbol', '2'))))
-  * optimized:
+  $ try 'sort(1^:2)'
   (func
     ('symbol', 'sort')
     (range
@@ -553,22 +530,14 @@
   1
   2
 
-  $ try --optimize '(3^:4)^:2'
-  (parent
-    (group
-      (parent
-        ('symbol', '3')
-        (rangepre
-          ('symbol', '4'))))
-    (rangepre
-      ('symbol', '2')))
-  * optimized:
+  $ try '(3^:4)^:2'
   (range
     (parentpost
-      (range
-        (parentpost
-          ('symbol', '3'))
-        ('symbol', '4')))
+      (group
+        (range
+          (parentpost
+            ('symbol', '3'))
+          ('symbol', '4'))))
     ('symbol', '2'))
   * set:
   <spanset+ 0:2>
@@ -576,22 +545,14 @@
   1
   2
 
-  $ try --optimize '(3^::4)^::2'
-  (parent
-    (group
-      (parent
-        ('symbol', '3')
-        (dagrangepre
-          ('symbol', '4'))))
-    (dagrangepre
-      ('symbol', '2')))
-  * optimized:
+  $ try '(3^::4)^::2'
   (dagrange
     (parentpost
-      (dagrange
-        (parentpost
-          ('symbol', '3'))
-        ('symbol', '4')))
+      (group
+        (dagrange
+          (parentpost
+            ('symbol', '3'))
+          ('symbol', '4'))))
     ('symbol', '2'))
   * set:
   <baseset+ [0, 1, 2]>
@@ -601,14 +562,9 @@
 
  x^ in alias should also be resolved
 
-  $ try --optimize 'A' --config 'revsetalias.A=1^:2'
+  $ try 'A' --config 'revsetalias.A=1^:2'
   ('symbol', 'A')
   * expanded:
-  (parent
-    ('symbol', '1')
-    (rangepre
-      ('symbol', '2')))
-  * optimized:
   (range
     (parentpost
       ('symbol', '1'))
@@ -619,7 +575,7 @@
   1
   2
 
-  $ try --optimize 'A:2' --config 'revsetalias.A=1^'
+  $ try 'A:2' --config 'revsetalias.A=1^'
   (range
     ('symbol', 'A')
     ('symbol', '2'))
@@ -628,11 +584,6 @@
     (parentpost
       ('symbol', '1'))
     ('symbol', '2'))
-  * optimized:
-  (range
-    (parentpost
-      ('symbol', '1'))
-    ('symbol', '2'))
   * set:
   <spanset+ 0:2>
   0
@@ -642,7 +593,7 @@
  but not beyond the boundary of alias expansion, because the resolution should
  be made at the parsing stage
 
-  $ try --optimize '1^A' --config 'revsetalias.A=:2'
+  $ try '1^A' --config 'revsetalias.A=:2'
   (parent
     ('symbol', '1')
     ('symbol', 'A'))
@@ -651,17 +602,8 @@
     ('symbol', '1')
     (rangepre
       ('symbol', '2')))
-  * optimized:
-  (range
-    (parentpost
-      ('symbol', '1'))
-    ('symbol', '2'))
-  * set:
-  <spanset+ 0:2>
-  0
-  1
-  2
-BROKEN: should be parsed as '1^(:2)'
+  hg: parse error: ^ expects a number 0, 1, or 2
+  [255]
 
 ancestor can accept 0 or more arguments