# HG changeset patch # User FUJIWARA Katsunori # Date 1420555578 -32400 # Node ID 3a4d8a6ce432044d00f1aeec2c95100a77f93129 # Parent f2893cd8d1e543a0baa3c378b222f8f3e4a9d638 revset: introduce new operator "##" to concatenate strings/symbols at runtime Before this patch, there is no way to concatenate strings at runtime. For example, to search for the issue ID "1234" in descriptions against all of "issue 1234", "issue:1234", issue1234" and "bug(1234)" patterns, the revset below should be written fully from scratch for each issue ID. grep(r"\bissue[ :]?1234\b|\bbug\(1234\)") This patch introduces new infix operator "##" to concatenate strings/symbols at runtime. Operator symbol "##" comes from the same one of C pre-processor. This concatenation allows parametrizing a part of strings in revset queries. In the case of example above, the definition of the revset alias using operator "##" below can search issue ID "1234" in complicated patterns by "issue(1234)" simply: issue($1) = grep(r"\bissue[ :]?" ## $1 ## r"\b|\bbug\(" ## $1 ## r"\)") "##" operator does: - concatenate not only strings but also symbols into the string Exact distinction between strings and symbols seems not to be convenience, because it is tiresome for users (and "revset.getstring" treats both similarly) For example of revset alias "issue()", "issue(1234)" is easier than "issue('1234')". - have higher priority than any other prefix, infix and postfix operators (like as "##" of C pre-processor) This patch (re-)assigns the priority 20 to "##", and 21 to "(", because priority 19 is already assigned to "-" as prefix "negate". diff -r f2893cd8d1e5 -r 3a4d8a6ce432 mercurial/commands.py --- a/mercurial/commands.py Fri Nov 28 19:50:52 2014 -0500 +++ b/mercurial/commands.py Tue Jan 06 23:46:18 2015 +0900 @@ -2840,6 +2840,10 @@ newtree = revset.findaliases(ui, tree) if newtree != tree: ui.note(revset.prettyformat(newtree), "\n") + tree = newtree + newtree = revset.foldconcat(tree) + if newtree != tree: + ui.note(revset.prettyformat(newtree), "\n") if opts["optimize"]: weight, optimizedtree = revset.optimize(newtree, True) ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n") diff -r f2893cd8d1e5 -r 3a4d8a6ce432 mercurial/help/revsets.txt --- a/mercurial/help/revsets.txt Fri Nov 28 19:50:52 2014 -0500 +++ b/mercurial/help/revsets.txt Tue Jan 06 23:46:18 2015 +0900 @@ -81,6 +81,19 @@ defines three aliases, ``h``, ``d``, and ``rs``. ``rs(0:tip, author)`` is exactly equivalent to ``reverse(sort(0:tip, author))``. +An infix operator ``##`` can concatenate strings and identifiers into +one string. For example:: + + [revsetalias] + issue($1) = grep(r'\bissue[ :]?' ## $1 ## r'\b|\bbug\(' ## $1 ## r'\)') + +``issue(1234)`` is equivalent to ``grep(r'\bissue[ :]?1234\b|\bbug\(1234\)')`` +in this case. This matches against all of "issue 1234", "issue:1234", +"issue1234" and "bug(1234)". + +All other prefix, infix and postfix operators have lower priority than +``##``. For example, ``$1 ## $2~2`` is equivalent to ``($1 ## $2)~2``. + Command line equivalents for :hg:`log`:: -f -> ::. diff -r f2893cd8d1e5 -r 3a4d8a6ce432 mercurial/revset.py --- a/mercurial/revset.py Fri Nov 28 19:50:52 2014 -0500 +++ b/mercurial/revset.py Tue Jan 06 23:46:18 2015 +0900 @@ -102,7 +102,8 @@ return baseset(sorted(reachable)) elements = { - "(": (20, ("group", 1, ")"), ("func", 1, ")")), + "(": (21, ("group", 1, ")"), ("func", 1, ")")), + "##": (20, None, ("_concat", 20)), "~": (18, None, ("ancestor", 18)), "^": (18, None, ("parent", 18), ("parentpost", 18)), "-": (5, ("negate", 19), ("minus", 5)), @@ -148,6 +149,9 @@ elif c == '.' and program[pos:pos + 2] == '..': # look ahead carefully yield ('..', None, pos) pos += 1 # skip ahead + elif c == '#' and program[pos:pos + 2] == '##': # look ahead carefully + yield ('##', None, pos) + pos += 1 # skip ahead elif c in "():,-|&+!~^": # handle simple operators yield (c, None, pos) elif (c in '"\'' or c == 'r' and @@ -2156,6 +2160,27 @@ alias.warned = True return tree +def foldconcat(tree): + """Fold elements to be concatenated by `##` + """ + if not isinstance(tree, tuple) or tree[0] in ('string', 'symbol'): + return tree + if tree[0] == '_concat': + pending = [tree] + l = [] + while pending: + e = pending.pop() + if e[0] == '_concat': + pending.extend(reversed(e[1:])) + elif e[0] in ('string', 'symbol'): + l.append(e[1]) + else: + msg = _("\"##\" can't concatenate \"%s\" element") % (e[0]) + raise error.ParseError(msg) + return ('string', ''.join(l)) + else: + return tuple(foldconcat(t) for t in tree) + def parse(spec, lookup=None): p = parser.parser(tokenize, elements) return p.parse(spec, lookup=lookup) @@ -2171,6 +2196,7 @@ raise error.ParseError(_("invalid token"), pos) if ui: tree = findaliases(ui, tree, showwarning=ui.warn) + tree = foldconcat(tree) weight, tree = optimize(tree, True) def mfunc(repo, subset): if util.safehasattr(subset, 'isascending'): diff -r f2893cd8d1e5 -r 3a4d8a6ce432 tests/test-revset.t --- a/tests/test-revset.t Fri Nov 28 19:50:52 2014 -0500 +++ b/tests/test-revset.t Tue Jan 06 23:46:18 2015 +0900 @@ -1123,6 +1123,54 @@ $ cd ../repo $ log 'remote(".a.b.c.", "../remote3")' +tests for concatenation of strings/symbols by "##" + + $ try "278 ## '5f5' ## 1ee ## 'ce5'" + (_concat + (_concat + (_concat + ('symbol', '278') + ('string', '5f5')) + ('symbol', '1ee')) + ('string', 'ce5')) + ('string', '2785f51eece5') + 0 + + $ echo 'cat4($1, $2, $3, $4) = $1 ## $2 ## $3 ## $4' >> .hg/hgrc + $ try "cat4(278, '5f5', 1ee, 'ce5')" + (func + ('symbol', 'cat4') + (list + (list + (list + ('symbol', '278') + ('string', '5f5')) + ('symbol', '1ee')) + ('string', 'ce5'))) + (_concat + (_concat + (_concat + ('symbol', '278') + ('string', '5f5')) + ('symbol', '1ee')) + ('string', 'ce5')) + ('string', '2785f51eece5') + 0 + +(check concatenation in alias nesting) + + $ echo 'cat2($1, $2) = $1 ## $2' >> .hg/hgrc + $ echo 'cat2x2($1, $2, $3, $4) = cat2($1 ## $2, $3 ## $4)' >> .hg/hgrc + $ log "cat2x2(278, '5f5', 1ee, 'ce5')" + 0 + +(check operator priority) + + $ echo 'cat2n2($1, $2, $3, $4) = $1 ## $2 or $3 ## $4~2' >> .hg/hgrc + $ log "cat2n2(2785f5, 1eece5, 24286f, 4ae135)" + 0 + 4 + $ cd .. test author/desc/keyword in problematic encoding