Patchwork [3,of,5] revset: resolve ambiguity of x^:y before alias expansion

login
register
mail settings
Submitter Yuya Nishihara
Date Aug. 8, 2016, 3:25 p.m.
Message ID <0d0857c1d633b0fc5ad8.1470669922@mimosa>
Download mbox | patch
Permalink /patch/16200/
State Accepted
Headers show

Comments

Yuya Nishihara - Aug. 8, 2016, 3:25 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1470482460 -32400
#      Sat Aug 06 20:21:00 2016 +0900
# Node ID 0d0857c1d633b0fc5ad8d8f3df08dc706dde8845
# Parent  3f08020d8a710f5a3bee7e88452faccacf800c6c
revset: resolve ambiguity of x^:y before alias expansion

This is purely a parsing problem, which should be resolved before alias
expansion.

Patch

diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -2314,6 +2314,23 @@  def _matchonly(revs, bases):
         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 @@  def _optimize(x, small):
     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 @@  def _parsewith(spec, lookup=None, symini
                                  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"""
diff --git a/tests/test-revset.t b/tests/test-revset.t
--- a/tests/test-revset.t
+++ b/tests/test-revset.t
@@ -482,12 +482,7 @@  infix/suffix resolution of ^ operator (i
 
  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 @@  infix/suffix resolution of ^ operator (i
   1
   2
 
-  $ try --optimize '1^::2'
-  (parent
-    ('symbol', '1')
-    (dagrangepre
-      ('symbol', '2')))
-  * optimized:
+  $ try '1^::2'
   (dagrange
     (parentpost
       ('symbol', '1'))
@@ -516,31 +506,18 @@  infix/suffix resolution of ^ operator (i
 
  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 @@  infix/suffix resolution of ^ operator (i
   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 @@  infix/suffix resolution of ^ operator (i
   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 @@  infix/suffix resolution of ^ operator (i
 
  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 @@  infix/suffix resolution of ^ operator (i
   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 @@  infix/suffix resolution of ^ operator (i
     (parentpost
       ('symbol', '1'))
     ('symbol', '2'))
-  * optimized:
-  (range
-    (parentpost
-      ('symbol', '1'))
-    ('symbol', '2'))
   * set:
   <spanset+ 0:2>
   0
@@ -642,7 +593,7 @@  infix/suffix resolution of ^ operator (i
  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 @@  infix/suffix resolution of ^ operator (i
     ('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