author | Matt Mackall <mpm@selenic.com> |
Fri, 10 Jun 2011 11:43:38 -0500 | |
changeset 14554 | 68db17047637 |
parent 14551 | 68d814a3cefd |
child 14673 | b0566467c492 |
permissions | -rw-r--r-- |
14511
30506b894359
filesets: introduce basic fileset expression parser
Matt Mackall <mpm@selenic.com>
parents:
14509
diff
changeset
|
1 |
# fileset.py - file set queries for mercurial |
11275 | 2 |
# |
3 |
# Copyright 2010 Matt Mackall <mpm@selenic.com> |
|
4 |
# |
|
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. |
|
7 |
||
14554 | 8 |
import parser, error |
13593
cc4721ed7a2a
help: extract items doc generation function
Patrick Mezard <pmezard@gmail.com>
parents:
13506
diff
changeset
|
9 |
from i18n import _ |
11275 | 10 |
|
11 |
elements = { |
|
12 |
"(": (20, ("group", 1, ")"), ("func", 1, ")")), |
|
12616
e797fdf91df4
revset: lower precedence of minus infix (issue2361)
Matt Mackall <mpm@selenic.com>
parents:
12615
diff
changeset
|
13 |
"-": (5, ("negate", 19), ("minus", 5)), |
11275 | 14 |
"not": (10, ("not", 10)), |
15 |
"!": (10, ("not", 10)), |
|
16 |
"and": (5, None, ("and", 5)), |
|
17 |
"&": (5, None, ("and", 5)), |
|
18 |
"or": (4, None, ("or", 4)), |
|
19 |
"|": (4, None, ("or", 4)), |
|
20 |
"+": (4, None, ("or", 4)), |
|
21 |
",": (2, None, ("list", 2)), |
|
22 |
")": (0, None, None), |
|
23 |
"symbol": (0, ("symbol",), None), |
|
24 |
"string": (0, ("string",), None), |
|
25 |
"end": (0, None, None), |
|
26 |
} |
|
27 |
||
28 |
keywords = set(['and', 'or', 'not']) |
|
29 |
||
14551
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
30 |
globchars = ".*{}[]?/\\" |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
31 |
|
11275 | 32 |
def tokenize(program): |
33 |
pos, l = 0, len(program) |
|
34 |
while pos < l: |
|
35 |
c = program[pos] |
|
36 |
if c.isspace(): # skip inter-token whitespace |
|
37 |
pass |
|
14511
30506b894359
filesets: introduce basic fileset expression parser
Matt Mackall <mpm@selenic.com>
parents:
14509
diff
changeset
|
38 |
elif c in "(),-|&+!": # handle simple operators |
11289
4215ce511134
revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents:
11284
diff
changeset
|
39 |
yield (c, None, pos) |
12408
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
40 |
elif (c in '"\'' or c == 'r' and |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
41 |
program[pos:pos + 2] in ("r'", 'r"')): # handle quoted strings |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
42 |
if c == 'r': |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
43 |
pos += 1 |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
44 |
c = program[pos] |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
45 |
decode = lambda x: x |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
46 |
else: |
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
47 |
decode = lambda x: x.decode('string-escape') |
11275 | 48 |
pos += 1 |
49 |
s = pos |
|
50 |
while pos < l: # find closing quote |
|
51 |
d = program[pos] |
|
52 |
if d == '\\': # skip over escaped characters |
|
53 |
pos += 2 |
|
54 |
continue |
|
55 |
if d == c: |
|
12408
78a97859b90d
revset: support raw string literals
Brodie Rao <brodie@bitheap.org>
parents:
12401
diff
changeset
|
56 |
yield ('string', decode(program[s:pos]), s) |
11275 | 57 |
break |
58 |
pos += 1 |
|
59 |
else: |
|
11383
de544774ebea
revset: all your error messages are belong to _
Martin Geisler <mg@lazybytes.net>
parents:
11349
diff
changeset
|
60 |
raise error.ParseError(_("unterminated string"), s) |
14551
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
61 |
elif c.isalnum() or c in globchars or ord(c) > 127: |
14513 | 62 |
# gather up a symbol/keyword |
11275 | 63 |
s = pos |
64 |
pos += 1 |
|
65 |
while pos < l: # find end of symbol |
|
66 |
d = program[pos] |
|
14551
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
67 |
if not (d.isalnum() or d in globchars or ord(d) > 127): |
11275 | 68 |
break |
69 |
pos += 1 |
|
70 |
sym = program[s:pos] |
|
71 |
if sym in keywords: # operator keywords |
|
11289
4215ce511134
revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents:
11284
diff
changeset
|
72 |
yield (sym, None, s) |
11275 | 73 |
else: |
11289
4215ce511134
revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents:
11284
diff
changeset
|
74 |
yield ('symbol', sym, s) |
11275 | 75 |
pos -= 1 |
76 |
else: |
|
11383
de544774ebea
revset: all your error messages are belong to _
Martin Geisler <mg@lazybytes.net>
parents:
11349
diff
changeset
|
77 |
raise error.ParseError(_("syntax error"), pos) |
11275 | 78 |
pos += 1 |
11289
4215ce511134
revset: raise ParseError exceptions
Matt Mackall <mpm@selenic.com>
parents:
11284
diff
changeset
|
79 |
yield ('end', None, pos) |
11275 | 80 |
|
81 |
parse = parser.parser(tokenize, elements).parse |
|
82 |
||
14551
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
83 |
def getstring(x, err): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
84 |
if x and (x[0] == 'string' or x[0] == 'symbol'): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
85 |
return x[1] |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
86 |
raise error.ParseError(err) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
87 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
88 |
def getset(mctx, x): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
89 |
if not x: |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
90 |
raise error.ParseError(_("missing argument")) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
91 |
return methods[x[0]](mctx, *x[1:]) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
92 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
93 |
def stringset(mctx, x): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
94 |
m = mctx.matcher([x]) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
95 |
return [f for f in mctx.subset if m(f)] |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
96 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
97 |
def andset(mctx, x, y): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
98 |
return getset(mctx.narrow(getset(mctx, x)), y) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
99 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
100 |
def orset(mctx, x, y): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
101 |
# needs optimizing |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
102 |
xl = getset(mctx, x) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
103 |
yl = getset(mctx, y) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
104 |
return xl + [f for f in yl if f not in xl] |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
105 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
106 |
def notset(mctx, x): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
107 |
s = set(getset(mctx, x)) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
108 |
return [r for r in mctx.subset if r not in s] |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
109 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
110 |
def listset(mctx, a, b): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
111 |
raise error.ParseError(_("can't use a list in this context")) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
112 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
113 |
methods = { |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
114 |
'string': stringset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
115 |
'symbol': stringset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
116 |
'and': andset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
117 |
'or': orset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
118 |
'list': listset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
119 |
'group': getset, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
120 |
'not': notset |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
121 |
} |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
122 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
123 |
class matchctx(object): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
124 |
def __init__(self, ctx, matchfn, subset=None): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
125 |
self.ctx = ctx |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
126 |
self.matchfn = matchfn |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
127 |
self.subset = subset |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
128 |
if subset is None: |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
129 |
self.subset = ctx.walk(matchfn([])) # optimize this later |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
130 |
def matcher(self, pattern): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
131 |
return self.matchfn(pattern) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
132 |
def filter(self, files): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
133 |
return [f for f in files if f in self.subset] |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
134 |
def narrow(self, files): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
135 |
return matchctx(self.ctx, self.matchfn, |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
136 |
self.filter(files)) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
137 |
|
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
138 |
def getfileset(ctx, matchfn, expr): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
139 |
tree, pos = parse(expr) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
140 |
if (pos != len(expr)): |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
141 |
raise error.ParseError("invalid token", pos) |
68d814a3cefd
fileset: basic pattern and boolean support
Matt Mackall <mpm@selenic.com>
parents:
14513
diff
changeset
|
142 |
return getset(matchctx(ctx, matchfn), tree) |