Patchwork [3,of,7,V3,RFC] parser: move alias declaration parser to common rule-set class

login
register
mail settings
Submitter Yuya Nishihara
Date April 3, 2016, 9:48 a.m.
Message ID <aa54435b4a485541d3be.1459676897@mimosa>
Download mbox | patch
Permalink /patch/14277/
State Accepted
Delegated to: Pierre-Yves David
Headers show

Comments

Yuya Nishihara - April 3, 2016, 9:48 a.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1456736043 -32400
#      Mon Feb 29 17:54:03 2016 +0900
# Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
# Parent  37cbd68f6cd8760b146570dd8b547589539605fd
parser: move alias declaration parser to common rule-set class

The original _parsealiasdecl() function is split into common _builddecl()
and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
call is temporarily replaced by rules._builddecl(), which should be eliminated
later.

The doctests are mostly ported by using the dummy parse(), but the test for
'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
Pierre-Yves David - April 3, 2016, 9:04 p.m.
On 04/03/2016 02:48 AM, Yuya Nishihara wrote:
> # HG changeset patch
> # User Yuya Nishihara <yuya@tcha.org>
> # Date 1456736043 -32400
> #      Mon Feb 29 17:54:03 2016 +0900
> # Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
> # Parent  37cbd68f6cd8760b146570dd8b547589539605fd
> parser: move alias declaration parser to common rule-set class
>
> The original _parsealiasdecl() function is split into common _builddecl()
> and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
> call is temporarily replaced by rules._builddecl(), which should be eliminated
> later.
>
> The doctests are mostly ported by using the dummy parse(), but the test for
> 'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
> working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
> handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
>

[…]

> @@ -2373,6 +2316,7 @@ def _parsealiasdefn(defn, args):
>   class _aliasrules(parser.basealiasrules):
>       """Parsing and expansion rule set of revset aliases"""
>       _section = _('revset alias')
> +    _parsedecl = staticmethod(_parsealiasdecl)
>       _getlist = staticmethod(getlist)
>
>   class revsetalias(object):

Why are we keeping the function outside of the class? (With a manual 
staticmethod promition and assignement). Shouln't we be able to directly 
declare it inside the class with a @static method decorator?

Cheers,
Yuya Nishihara - April 4, 2016, 12:27 p.m.
On Sun, 3 Apr 2016 14:04:20 -0700, Pierre-Yves David wrote:
> On 04/03/2016 02:48 AM, Yuya Nishihara wrote:
> > # HG changeset patch
> > # User Yuya Nishihara <yuya@tcha.org>
> > # Date 1456736043 -32400
> > #      Mon Feb 29 17:54:03 2016 +0900
> > # Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
> > # Parent  37cbd68f6cd8760b146570dd8b547589539605fd
> > parser: move alias declaration parser to common rule-set class
> >
> > The original _parsealiasdecl() function is split into common _builddecl()
> > and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
> > call is temporarily replaced by rules._builddecl(), which should be eliminated
> > later.
> >
> > The doctests are mostly ported by using the dummy parse(), but the test for
> > 'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
> > working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
> > handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
> >  
> 
> […]
> 
> > @@ -2373,6 +2316,7 @@ def _parsealiasdefn(defn, args):
> >   class _aliasrules(parser.basealiasrules):
> >       """Parsing and expansion rule set of revset aliases"""
> >       _section = _('revset alias')
> > +    _parsedecl = staticmethod(_parsealiasdecl)
> >       _getlist = staticmethod(getlist)
> >
> >   class revsetalias(object):  
> 
> Why are we keeping the function outside of the class? (With a manual
> staticmethod promition and assignement). Shouln't we be able to directly
> declare it inside the class with a @static method decorator?

Two reasons:

 a) It's paired with _tokenizealias(). I could move both _parsealiasdecl()
    and _tokenizealias() to _aliasrules, but it would introduce unnecessary
    name "_tokenize" under _aliasrules, which I didn't want to.

 b) _parsealiasdecl() is only a parser that takes '$' as a symbol char, which
    can theoretically be used as one of parsing functions.
Pierre-Yves David - April 11, 2016, 6:16 a.m.
On 04/04/2016 05:27 AM, Yuya Nishihara wrote:
> On Sun, 3 Apr 2016 14:04:20 -0700, Pierre-Yves David wrote:
>> On 04/03/2016 02:48 AM, Yuya Nishihara wrote:
>>> # HG changeset patch
>>> # User Yuya Nishihara <yuya@tcha.org>
>>> # Date 1456736043 -32400
>>> #      Mon Feb 29 17:54:03 2016 +0900
>>> # Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
>>> # Parent  37cbd68f6cd8760b146570dd8b547589539605fd
>>> parser: move alias declaration parser to common rule-set class
>>>
>>> The original _parsealiasdecl() function is split into common _builddecl()
>>> and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
>>> call is temporarily replaced by rules._builddecl(), which should be eliminated
>>> later.
>>>
>>> The doctests are mostly ported by using the dummy parse(), but the test for
>>> 'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
>>> working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
>>> handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
>>>   
>> […]
>>
>>> @@ -2373,6 +2316,7 @@ def _parsealiasdefn(defn, args):
>>>    class _aliasrules(parser.basealiasrules):
>>>        """Parsing and expansion rule set of revset aliases"""
>>>        _section = _('revset alias')
>>> +    _parsedecl = staticmethod(_parsealiasdecl)
>>>        _getlist = staticmethod(getlist)
>>>
>>>    class revsetalias(object):
>> Why are we keeping the function outside of the class? (With a manual
>> staticmethod promition and assignement). Shouln't we be able to directly
>> declare it inside the class with a @static method decorator?
> Two reasons:
>
>   a) It's paired with _tokenizealias(). I could move both _parsealiasdecl()
>      and _tokenizealias() to _aliasrules, but it would introduce unnecessary
>      name "_tokenize" under _aliasrules, which I didn't want to.

I do not see why this matters here. Having `parsealiasdecl` directly 
declared as a static method will not prevent the use of that external 
function `tokenizealias `. Does it? Did I missed something else?

>   b) _parsealiasdecl() is only a parser that takes '$' as a symbol char, which
>      can theoretically be used as one of parsing functions.

I did not understand what you are trying to tell me here. Can you rephrase?
Yuya Nishihara - April 11, 2016, 11:50 a.m.
On Sun, 10 Apr 2016 23:16:32 -0700, Pierre-Yves David wrote:
> On 04/04/2016 05:27 AM, Yuya Nishihara wrote:
> > On Sun, 3 Apr 2016 14:04:20 -0700, Pierre-Yves David wrote:  
> >> On 04/03/2016 02:48 AM, Yuya Nishihara wrote:  
> >>> # HG changeset patch
> >>> # User Yuya Nishihara <yuya@tcha.org>
> >>> # Date 1456736043 -32400
> >>> #      Mon Feb 29 17:54:03 2016 +0900
> >>> # Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
> >>> # Parent  37cbd68f6cd8760b146570dd8b547589539605fd
> >>> parser: move alias declaration parser to common rule-set class
> >>>
> >>> The original _parsealiasdecl() function is split into common _builddecl()
> >>> and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
> >>> call is temporarily replaced by rules._builddecl(), which should be eliminated
> >>> later.
> >>>
> >>> The doctests are mostly ported by using the dummy parse(), but the test for
> >>> 'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
> >>> working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
> >>> handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
> >>>     
> >> […]
> >>  
> >>> @@ -2373,6 +2316,7 @@ def _parsealiasdefn(defn, args):
> >>>    class _aliasrules(parser.basealiasrules):
> >>>        """Parsing and expansion rule set of revset aliases"""
> >>>        _section = _('revset alias')
> >>> +    _parsedecl = staticmethod(_parsealiasdecl)
> >>>        _getlist = staticmethod(getlist)
> >>>
> >>>    class revsetalias(object):  
> >> Why are we keeping the function outside of the class? (With a manual
> >> staticmethod promition and assignement). Shouln't we be able to directly
> >> declare it inside the class with a @static method decorator?  
> > Two reasons:
> >
> >   a) It's paired with _tokenizealias(). I could move both _parsealiasdecl()
> >      and _tokenizealias() to _aliasrules, but it would introduce unnecessary
> >      name "_tokenize" under _aliasrules, which I didn't want to.  
> 
> I do not see why this matters here. Having `parsealiasdecl` directly 
> declared as a static method will not prevent the use of that external 
> function `tokenizealias `. Does it? Did I missed something else?

No. I mean there isn't much motivation to move parsealiasdecl to
_aliasrules._parsedecl. It could, but that should be totally fine to keep
it as a free function.

> >   b) _parsealiasdecl() is only a parser that takes '$' as a symbol char, which
> >      can theoretically be used as one of parsing functions.  
> 
> I did not understand what you are trying to tell me here. Can you rephrase?

_parsealiasdecl() isn't strongly coupled with _aliasrules. It's more like
free parsing tools such as parse() or getlist().

Anyway, this is trivial matter, I don't think it's worth arguing about this.
If you prefer defining static function inside class, I'm okay to do that by
follow-up patch.
Pierre-Yves David - April 13, 2016, 5:59 a.m.
On 04/11/2016 04:50 AM, Yuya Nishihara wrote:
> On Sun, 10 Apr 2016 23:16:32 -0700, Pierre-Yves David wrote:
>> On 04/04/2016 05:27 AM, Yuya Nishihara wrote:
>>> On Sun, 3 Apr 2016 14:04:20 -0700, Pierre-Yves David wrote:
>>>> On 04/03/2016 02:48 AM, Yuya Nishihara wrote:
>>>>> # HG changeset patch
>>>>> # User Yuya Nishihara <yuya@tcha.org>
>>>>> # Date 1456736043 -32400
>>>>> #      Mon Feb 29 17:54:03 2016 +0900
>>>>> # Node ID aa54435b4a485541d3be7abbe5ed6fd584b43ee5
>>>>> # Parent  37cbd68f6cd8760b146570dd8b547589539605fd
>>>>> parser: move alias declaration parser to common rule-set class
>>>>>
>>>>> The original _parsealiasdecl() function is split into common _builddecl()
>>>>> and revset-specific _parsealiasdecl(). And the original _parsealiasdecl()
>>>>> call is temporarily replaced by rules._builddecl(), which should be eliminated
>>>>> later.
>>>>>
>>>>> The doctests are mostly ported by using the dummy parse(), but the test for
>>>>> 'foo bar' is kept in _parsealiasdecl() as it checks if "pos != len(decl)" is
>>>>> working. Also, 'foo($1)' test is added to make sure the alias tokenizer can
>>>>> handle '$1' symbol, which is the only reason why we need _parsealiasdecl().
>>>>>      
>>>> […]
>>>>   
>>>>> @@ -2373,6 +2316,7 @@ def _parsealiasdefn(defn, args):
>>>>>     class _aliasrules(parser.basealiasrules):
>>>>>         """Parsing and expansion rule set of revset aliases"""
>>>>>         _section = _('revset alias')
>>>>> +    _parsedecl = staticmethod(_parsealiasdecl)
>>>>>         _getlist = staticmethod(getlist)
>>>>>
>>>>>     class revsetalias(object):
>>>> Why are we keeping the function outside of the class? (With a manual
>>>> staticmethod promition and assignement). Shouln't we be able to directly
>>>> declare it inside the class with a @static method decorator?
>>> Two reasons:
>>>
>>>    a) It's paired with _tokenizealias(). I could move both _parsealiasdecl()
>>>       and _tokenizealias() to _aliasrules, but it would introduce unnecessary
>>>       name "_tokenize" under _aliasrules, which I didn't want to.
>> I do not see why this matters here. Having `parsealiasdecl` directly
>> declared as a static method will not prevent the use of that external
>> function `tokenizealias `. Does it? Did I missed something else?
> No. I mean there isn't much motivation to move parsealiasdecl to
> _aliasrules._parsedecl. It could, but that should be totally fine to keep
> it as a free function.

If only the class use them, I would prefer to see them explicitly in the 
class

>>>    b) _parsealiasdecl() is only a parser that takes '$' as a symbol char, which
>>>       can theoretically be used as one of parsing functions.
>> I did not understand what you are trying to tell me here. Can you rephrase?
> _parsealiasdecl() isn't strongly coupled with _aliasrules. It's more like
> free parsing tools such as parse() or getlist().
>
> Anyway, this is trivial matter, I don't think it's worth arguing about this.
> If you prefer defining static function inside class, I'm okay to do that by
> follow-up patch.

I agree this have been delayed for too long, I've pushed that full series.

Thanks
Yuya Nishihara - April 13, 2016, 2:25 p.m.
On Tue, 12 Apr 2016 22:59:42 -0700, Pierre-Yves David wrote:
> On 04/11/2016 04:50 AM, Yuya Nishihara wrote:
> >> I do not see why this matters here. Having `parsealiasdecl` directly
> >> declared as a static method will not prevent the use of that external
> >> function `tokenizealias `. Does it? Did I missed something else?  
> > No. I mean there isn't much motivation to move parsealiasdecl to
> > _aliasrules._parsedecl. It could, but that should be totally fine to keep
> > it as a free function.  
> 
> If only the class use them, I would prefer to see them explicitly in the 
> class

I came up with an idea that can convince me to move _parsealias() to class.
I will sent it after 3.8 release. I have 3 more series for template aliases,
which I hopefully want to bring in 3.8.

Patch

diff --git a/mercurial/parser.py b/mercurial/parser.py
--- a/mercurial/parser.py
+++ b/mercurial/parser.py
@@ -264,3 +264,108 @@  class basealiasrules(object):
     def _getlist(tree):
         """Extract a list of arguments from parsed tree"""
         raise NotImplementedError
+
+    @classmethod
+    def _builddecl(cls, decl):
+        """Parse an alias declaration into ``(name, tree, args, errorstr)``
+
+        This function analyzes the parsed tree. The parsing rule is provided
+        by ``_parsedecl()``.
+
+        - ``name``: of declared alias (may be ``decl`` itself at error)
+        - ``tree``: parse result (or ``None`` at error)
+        - ``args``: list of argument names (or None for symbol declaration)
+        - ``errorstr``: detail about detected error (or None)
+
+        >>> sym = lambda x: ('symbol', x)
+        >>> symlist = lambda *xs: ('list',) + tuple(sym(x) for x in xs)
+        >>> func = lambda n, a: ('func', sym(n), a)
+        >>> parsemap = {
+        ...     'foo': sym('foo'),
+        ...     '$foo': sym('$foo'),
+        ...     'foo::bar': ('dagrange', sym('foo'), sym('bar')),
+        ...     'foo()': func('foo', None),
+        ...     '$foo()': func('$foo', None),
+        ...     'foo($1, $2)': func('foo', symlist('$1', '$2')),
+        ...     'foo(bar_bar, baz.baz)':
+        ...         func('foo', symlist('bar_bar', 'baz.baz')),
+        ...     'foo(bar($1, $2))':
+        ...         func('foo', func('bar', symlist('$1', '$2'))),
+        ...     'foo($1, $2, nested($1, $2))':
+        ...         func('foo', (symlist('$1', '$2') +
+        ...                      (func('nested', symlist('$1', '$2')),))),
+        ...     'foo("bar")': func('foo', ('string', 'bar')),
+        ...     'foo($1, $2': error.ParseError('unexpected token: end', 10),
+        ...     'foo("bar': error.ParseError('unterminated string', 5),
+        ...     'foo($1, $2, $1)': func('foo', symlist('$1', '$2', '$1')),
+        ... }
+        >>> def parse(expr):
+        ...     x = parsemap[expr]
+        ...     if isinstance(x, Exception):
+        ...         raise x
+        ...     return x
+        >>> def getlist(tree):
+        ...     if not tree:
+        ...         return []
+        ...     if tree[0] == 'list':
+        ...         return list(tree[1:])
+        ...     return [tree]
+        >>> class aliasrules(basealiasrules):
+        ...     _parsedecl = staticmethod(parse)
+        ...     _getlist = staticmethod(getlist)
+        >>> builddecl = aliasrules._builddecl
+        >>> builddecl('foo')
+        ('foo', ('symbol', 'foo'), None, None)
+        >>> builddecl('$foo')
+        ('$foo', None, None, "'$' not for alias arguments")
+        >>> builddecl('foo::bar')
+        ('foo::bar', None, None, 'invalid format')
+        >>> builddecl('foo()')
+        ('foo', ('func', ('symbol', 'foo')), [], None)
+        >>> builddecl('$foo()')
+        ('$foo()', None, None, "'$' not for alias arguments")
+        >>> builddecl('foo($1, $2)')
+        ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
+        >>> builddecl('foo(bar_bar, baz.baz)')
+        ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
+        >>> builddecl('foo($1, $2, nested($1, $2))')
+        ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
+        >>> builddecl('foo(bar($1, $2))')
+        ('foo(bar($1, $2))', None, None, 'invalid argument list')
+        >>> builddecl('foo("bar")')
+        ('foo("bar")', None, None, 'invalid argument list')
+        >>> builddecl('foo($1, $2')
+        ('foo($1, $2', None, None, 'at 10: unexpected token: end')
+        >>> builddecl('foo("bar')
+        ('foo("bar', None, None, 'at 5: unterminated string')
+        >>> builddecl('foo($1, $2, $1)')
+        ('foo', None, None, 'argument names collide with each other')
+        """
+        try:
+            tree = cls._parsedecl(decl)
+        except error.ParseError as inst:
+            return (decl, None, None, parseerrordetail(inst))
+
+        if tree[0] == cls._symbolnode:
+            # "name = ...." style
+            name = tree[1]
+            if name.startswith('$'):
+                return (decl, None, None, _("'$' not for alias arguments"))
+            return (name, tree, None, None)
+
+        if tree[0] == cls._funcnode and tree[1][0] == cls._symbolnode:
+            # "name(arg, ....) = ...." style
+            name = tree[1][1]
+            if name.startswith('$'):
+                return (decl, None, None, _("'$' not for alias arguments"))
+            args = []
+            for arg in cls._getlist(tree[2]):
+                if arg[0] != cls._symbolnode:
+                    return (decl, None, None, _("invalid argument list"))
+                args.append(arg[1])
+            if len(args) != len(set(args)):
+                return (name, None, None,
+                        _("argument names collide with each other"))
+            return (name, tree[:2], args, None)
+
+        return (decl, None, None, _("invalid format"))
diff --git a/mercurial/revset.py b/mercurial/revset.py
--- a/mercurial/revset.py
+++ b/mercurial/revset.py
@@ -2241,75 +2241,18 @@  def _tokenizealias(program):
 def _parsealiasdecl(decl):
     """Parse alias declaration ``decl``
 
-    This returns ``(name, tree, args, errorstr)`` tuple:
-
-    - ``name``: of declared alias (may be ``decl`` itself at error)
-    - ``tree``: parse result (or ``None`` at error)
-    - ``args``: list of alias argument names (or None for symbol declaration)
-    - ``errorstr``: detail about detected error (or None)
-
-    >>> _parsealiasdecl('foo')
-    ('foo', ('symbol', 'foo'), None, None)
-    >>> _parsealiasdecl('$foo')
-    ('$foo', None, None, "'$' not for alias arguments")
-    >>> _parsealiasdecl('foo::bar')
-    ('foo::bar', None, None, 'invalid format')
+    >>> _parsealiasdecl('foo($1)')
+    ('func', ('symbol', 'foo'), ('symbol', '$1'))
     >>> _parsealiasdecl('foo bar')
-    ('foo bar', None, None, 'at 4: invalid token')
-    >>> _parsealiasdecl('foo()')
-    ('foo', ('func', ('symbol', 'foo')), [], None)
-    >>> _parsealiasdecl('$foo()')
-    ('$foo()', None, None, "'$' not for alias arguments")
-    >>> _parsealiasdecl('foo($1, $2)')
-    ('foo', ('func', ('symbol', 'foo')), ['$1', '$2'], None)
-    >>> _parsealiasdecl('foo(bar_bar, baz.baz)')
-    ('foo', ('func', ('symbol', 'foo')), ['bar_bar', 'baz.baz'], None)
-    >>> _parsealiasdecl('foo($1, $2, nested($1, $2))')
-    ('foo($1, $2, nested($1, $2))', None, None, 'invalid argument list')
-    >>> _parsealiasdecl('foo(bar($1, $2))')
-    ('foo(bar($1, $2))', None, None, 'invalid argument list')
-    >>> _parsealiasdecl('foo("string")')
-    ('foo("string")', None, None, 'invalid argument list')
-    >>> _parsealiasdecl('foo($1, $2')
-    ('foo($1, $2', None, None, 'at 10: unexpected token: end')
-    >>> _parsealiasdecl('foo("string')
-    ('foo("string', None, None, 'at 5: unterminated string')
-    >>> _parsealiasdecl('foo($1, $2, $1)')
-    ('foo', None, None, 'argument names collide with each other')
+    Traceback (most recent call last):
+      ...
+    ParseError: ('invalid token', 4)
     """
     p = parser.parser(elements)
-    try:
-        tree, pos = p.parse(_tokenizealias(decl))
-        if (pos != len(decl)):
-            raise error.ParseError(_('invalid token'), pos)
-        tree = parser.simplifyinfixops(tree, ('list',))
-    except error.ParseError as inst:
-        return (decl, None, None, parser.parseerrordetail(inst))
-
-    if True:  # XXX to be removed
-        if tree[0] == 'symbol':
-            # "name = ...." style
-            name = tree[1]
-            if name.startswith('$'):
-                return (decl, None, None, _("'$' not for alias arguments"))
-            return (name, tree, None, None)
-
-        if tree[0] == 'func' and tree[1][0] == 'symbol':
-            # "name(arg, ....) = ...." style
-            name = tree[1][1]
-            if name.startswith('$'):
-                return (decl, None, None, _("'$' not for alias arguments"))
-            args = []
-            for arg in getlist(tree[2]):
-                if arg[0] != 'symbol':
-                    return (decl, None, None, _("invalid argument list"))
-                args.append(arg[1])
-            if len(args) != len(set(args)):
-                return (name, None, None,
-                        _("argument names collide with each other"))
-            return (name, tree[:2], args, None)
-
-        return (decl, None, None, _("invalid format"))
+    tree, pos = p.parse(_tokenizealias(decl))
+    if pos != len(decl):
+        raise error.ParseError(_('invalid token'), pos)
+    return parser.simplifyinfixops(tree, ('list',))
 
 def _relabelaliasargs(tree, args):
     if not isinstance(tree, tuple):
@@ -2373,6 +2316,7 @@  def _parsealiasdefn(defn, args):
 class _aliasrules(parser.basealiasrules):
     """Parsing and expansion rule set of revset aliases"""
     _section = _('revset alias')
+    _parsedecl = staticmethod(_parsealiasdecl)
     _getlist = staticmethod(getlist)
 
 class revsetalias(object):
@@ -2386,7 +2330,8 @@  class revsetalias(object):
         h = heads(default)
         b($1) = ancestors($1) - ancestors(default)
         '''
-        self.name, self.tree, self.args, self.error = _parsealiasdecl(name)
+        r = _aliasrules._builddecl(name)
+        self.name, self.tree, self.args, self.error = r
         if self.error:
             self.error = _('failed to parse the declaration of revset alias'
                            ' "%s": %s') % (self.name, self.error)