filesets: introduce basic fileset expression parser
authorMatt Mackall <mpm@selenic.com>
Wed, 01 Jun 2011 19:12:18 -0500
changeset 14511 30506b894359
parent 14510 eccbb9980ada
child 14512 8c8b55733cbd
filesets: introduce basic fileset expression parser
mercurial/commands.py
mercurial/fileset.py
tests/test-debugcomplete.t
--- a/mercurial/commands.py	Thu Jun 02 00:43:34 2011 +0300
+++ b/mercurial/commands.py	Wed Jun 01 19:12:18 2011 -0500
@@ -13,7 +13,7 @@
 import patch, help, url, encoding, templatekw, discovery
 import archival, changegroup, cmdutil, sshserver, hbisect, hgweb, hgweb.server
 import merge as mergemod
-import minirst, revset
+import minirst, revset, fileset
 import dagparser, context, simplemerge
 import random, setdiscovery, treediscovery, dagutil
 
@@ -1597,6 +1597,13 @@
         localrevs = opts.get('local_head')
         doit(localrevs, remoterevs)
 
+@command('debugfileset', [], ('REVSPEC'))
+def debugfileset(ui, repo, expr):
+    '''parse and apply a fileset specification'''
+    if ui.verbose:
+        tree = fileset.parse(expr)[0]
+        ui.note(tree, "\n")
+
 @command('debugfsinfo', [], _('[PATH]'))
 def debugfsinfo(ui, path = "."):
     """show information detected about current filesystem"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial/fileset.py	Wed Jun 01 19:12:18 2011 -0500
@@ -0,0 +1,79 @@
+# fileset.py - file set queries for mercurial
+#
+# Copyright 2010 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2 or any later version.
+
+import parser, error
+from i18n import _
+
+elements = {
+    "(": (20, ("group", 1, ")"), ("func", 1, ")")),
+    "-": (5, ("negate", 19), ("minus", 5)),
+    "not": (10, ("not", 10)),
+    "!": (10, ("not", 10)),
+    "and": (5, None, ("and", 5)),
+    "&": (5, None, ("and", 5)),
+    "or": (4, None, ("or", 4)),
+    "|": (4, None, ("or", 4)),
+    "+": (4, None, ("or", 4)),
+    ",": (2, None, ("list", 2)),
+    ")": (0, None, None),
+    "symbol": (0, ("symbol",), None),
+    "string": (0, ("string",), None),
+    "end": (0, None, None),
+}
+
+keywords = set(['and', 'or', 'not'])
+
+def tokenize(program):
+    pos, l = 0, len(program)
+    while pos < l:
+        c = program[pos]
+        if c.isspace(): # skip inter-token whitespace
+            pass
+        elif c in "(),-|&+!": # handle simple operators
+            yield (c, None, pos)
+        elif (c in '"\'' or c == 'r' and
+              program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings
+            if c == 'r':
+                pos += 1
+                c = program[pos]
+                decode = lambda x: x
+            else:
+                decode = lambda x: x.decode('string-escape')
+            pos += 1
+            s = pos
+            while pos < l: # find closing quote
+                d = program[pos]
+                if d == '\\': # skip over escaped characters
+                    pos += 2
+                    continue
+                if d == c:
+                    yield ('string', decode(program[s:pos]), s)
+                    break
+                pos += 1
+            else:
+                raise error.ParseError(_("unterminated string"), s)
+        elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127: # gather up a symbol/keyword
+            s = pos
+            pos += 1
+            while pos < l: # find end of symbol
+                d = program[pos]
+                if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127):
+                    break
+                pos += 1
+            sym = program[s:pos]
+            if sym in keywords: # operator keywords
+                yield (sym, None, s)
+            else:
+                yield ('symbol', sym, s)
+            pos -= 1
+        else:
+            raise error.ParseError(_("syntax error"), pos)
+        pos += 1
+    yield ('end', None, pos)
+
+parse = parser.parser(tokenize, elements).parse
+
--- a/tests/test-debugcomplete.t	Thu Jun 02 00:43:34 2011 +0300
+++ b/tests/test-debugcomplete.t	Wed Jun 01 19:12:18 2011 -0500
@@ -76,6 +76,7 @@
   debugdata
   debugdate
   debugdiscovery
+  debugfileset
   debugfsinfo
   debuggetbundle
   debugignore
@@ -222,6 +223,7 @@
   debugdata: changelog, manifest
   debugdate: extended
   debugdiscovery: old, nonheads, ssh, remotecmd, insecure
+  debugfileset: 
   debugfsinfo: 
   debuggetbundle: head, common, type
   debugignore: