comparison mercurial/fileset.py @ 14551:68d814a3cefd

fileset: basic pattern and boolean support debugfileset can now generate file lists for things like: "* and not hg*"
author Matt Mackall <mpm@selenic.com>
date Wed, 08 Jun 2011 13:44:41 -0500
parents 85fe676c27e9
children 68db17047637
comparison
equal deleted inserted replaced
14550:2425a3536396 14551:68d814a3cefd
3 # Copyright 2010 Matt Mackall <mpm@selenic.com> 3 # Copyright 2010 Matt Mackall <mpm@selenic.com>
4 # 4 #
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 import parser, error 8 import parser, error, match
9 from i18n import _ 9 from i18n import _
10 10
11 elements = { 11 elements = {
12 "(": (20, ("group", 1, ")"), ("func", 1, ")")), 12 "(": (20, ("group", 1, ")"), ("func", 1, ")")),
13 "-": (5, ("negate", 19), ("minus", 5)), 13 "-": (5, ("negate", 19), ("minus", 5)),
24 "string": (0, ("string",), None), 24 "string": (0, ("string",), None),
25 "end": (0, None, None), 25 "end": (0, None, None),
26 } 26 }
27 27
28 keywords = set(['and', 'or', 'not']) 28 keywords = set(['and', 'or', 'not'])
29
30 globchars = ".*{}[]?/\\"
29 31
30 def tokenize(program): 32 def tokenize(program):
31 pos, l = 0, len(program) 33 pos, l = 0, len(program)
32 while pos < l: 34 while pos < l:
33 c = program[pos] 35 c = program[pos]
54 yield ('string', decode(program[s:pos]), s) 56 yield ('string', decode(program[s:pos]), s)
55 break 57 break
56 pos += 1 58 pos += 1
57 else: 59 else:
58 raise error.ParseError(_("unterminated string"), s) 60 raise error.ParseError(_("unterminated string"), s)
59 elif c.isalnum() or c in '.*{}[]?' or ord(c) > 127: 61 elif c.isalnum() or c in globchars or ord(c) > 127:
60 # gather up a symbol/keyword 62 # gather up a symbol/keyword
61 s = pos 63 s = pos
62 pos += 1 64 pos += 1
63 while pos < l: # find end of symbol 65 while pos < l: # find end of symbol
64 d = program[pos] 66 d = program[pos]
65 if not (d.isalnum() or d in ".*{}[]?," or ord(d) > 127): 67 if not (d.isalnum() or d in globchars or ord(d) > 127):
66 break 68 break
67 pos += 1 69 pos += 1
68 sym = program[s:pos] 70 sym = program[s:pos]
69 if sym in keywords: # operator keywords 71 if sym in keywords: # operator keywords
70 yield (sym, None, s) 72 yield (sym, None, s)
76 pos += 1 78 pos += 1
77 yield ('end', None, pos) 79 yield ('end', None, pos)
78 80
79 parse = parser.parser(tokenize, elements).parse 81 parse = parser.parser(tokenize, elements).parse
80 82
83 def getstring(x, err):
84 if x and (x[0] == 'string' or x[0] == 'symbol'):
85 return x[1]
86 raise error.ParseError(err)
87
88 def getset(mctx, x):
89 if not x:
90 raise error.ParseError(_("missing argument"))
91 return methods[x[0]](mctx, *x[1:])
92
93 def stringset(mctx, x):
94 m = mctx.matcher([x])
95 return [f for f in mctx.subset if m(f)]
96
97 def andset(mctx, x, y):
98 return getset(mctx.narrow(getset(mctx, x)), y)
99
100 def orset(mctx, x, y):
101 # needs optimizing
102 xl = getset(mctx, x)
103 yl = getset(mctx, y)
104 return xl + [f for f in yl if f not in xl]
105
106 def notset(mctx, x):
107 s = set(getset(mctx, x))
108 return [r for r in mctx.subset if r not in s]
109
110 def listset(mctx, a, b):
111 raise error.ParseError(_("can't use a list in this context"))
112
113 methods = {
114 'string': stringset,
115 'symbol': stringset,
116 'and': andset,
117 'or': orset,
118 'list': listset,
119 'group': getset,
120 'not': notset
121 }
122
123 class matchctx(object):
124 def __init__(self, ctx, matchfn, subset=None):
125 self.ctx = ctx
126 self.matchfn = matchfn
127 self.subset = subset
128 if subset is None:
129 self.subset = ctx.walk(matchfn([])) # optimize this later
130 def matcher(self, pattern):
131 return self.matchfn(pattern)
132 def filter(self, files):
133 return [f for f in files if f in self.subset]
134 def narrow(self, files):
135 return matchctx(self.ctx, self.matchfn,
136 self.filter(files))
137
138 def getfileset(ctx, matchfn, expr):
139 tree, pos = parse(expr)
140 if (pos != len(expr)):
141 raise error.ParseError("invalid token", pos)
142 return getset(matchctx(ctx, matchfn), tree)