comparison mercurial/filesetlang.py @ 38918:e79a69af1593

fileset: insert hints where status should be computed This will allow us to compute status against a narrowed set of files. For example, "path:build/ & (unknown() + missing())" is rewritten as "path:build/ & <withstatus>(unknown() + missing(), 'unknown missing')", and the status call can be narrowed by the left-hand-side matcher, "path:build/". mctx.buildstatus() calls will be solely processed by getmatchwithstatus().
author Yuya Nishihara <yuya@tcha.org>
date Sat, 21 Jul 2018 20:27:53 +0900
parents 899b4c74209c
children 2372284d9457
comparison
equal deleted inserted replaced
38917:0f56d08e6271 38918:e79a69af1593
169 getsymbol(x[1]) # function name must be a symbol 169 getsymbol(x[1]) # function name must be a symbol
170 ta = _analyze(x[2]) 170 ta = _analyze(x[2])
171 return (op, x[1], ta) 171 return (op, x[1], ta)
172 raise error.ProgrammingError('invalid operator %r' % op) 172 raise error.ProgrammingError('invalid operator %r' % op)
173 173
174 def _insertstatushints(x):
175 """Insert hint nodes where status should be calculated (first path)
176
177 This works in bottom-up way, summing up status names and inserting hint
178 nodes at 'and' and 'or' as needed. Thus redundant hint nodes may be left.
179
180 Returns (status-names, new-tree) at the given subtree, where status-names
181 is a sum of status names referenced in the given subtree.
182 """
183 if x is None:
184 return (), x
185
186 op = x[0]
187 if op in {'string', 'symbol', 'kindpat'}:
188 return (), x
189 if op == 'not':
190 h, t = _insertstatushints(x[1])
191 return h, (op, t)
192 if op == 'and':
193 ha, ta = _insertstatushints(x[1])
194 hb, tb = _insertstatushints(x[2])
195 hr = ha + hb
196 if ha and hb:
197 return hr, ('withstatus', (op, ta, tb), ('string', ' '.join(hr)))
198 return hr, (op, ta, tb)
199 if op == 'or':
200 hs, ts = zip(*(_insertstatushints(y) for y in x[1:]))
201 hr = sum(hs, ())
202 if sum(bool(h) for h in hs) > 1:
203 return hr, ('withstatus', (op,) + ts, ('string', ' '.join(hr)))
204 return hr, (op,) + ts
205 if op == 'list':
206 hs, ts = zip(*(_insertstatushints(y) for y in x[1:]))
207 return sum(hs, ()), (op,) + ts
208 if op == 'func':
209 f = getsymbol(x[1])
210 # don't propagate 'ha' crossing a function boundary
211 ha, ta = _insertstatushints(x[2])
212 if getattr(symbols.get(f), '_callstatus', False):
213 return (f,), ('withstatus', (op, x[1], ta), ('string', f))
214 return (), (op, x[1], ta)
215 raise error.ProgrammingError('invalid operator %r' % op)
216
217 def _mergestatushints(x, instatus):
218 """Remove redundant status hint nodes (second path)
219
220 This is the top-down path to eliminate inner hint nodes.
221 """
222 if x is None:
223 return x
224
225 op = x[0]
226 if op == 'withstatus':
227 if instatus:
228 # drop redundant hint node
229 return _mergestatushints(x[1], instatus)
230 t = _mergestatushints(x[1], instatus=True)
231 return (op, t, x[2])
232 if op in {'string', 'symbol', 'kindpat'}:
233 return x
234 if op == 'not':
235 t = _mergestatushints(x[1], instatus)
236 return (op, t)
237 if op == 'and':
238 ta = _mergestatushints(x[1], instatus)
239 tb = _mergestatushints(x[2], instatus)
240 return (op, ta, tb)
241 if op in {'list', 'or'}:
242 ts = tuple(_mergestatushints(y, instatus) for y in x[1:])
243 return (op,) + ts
244 if op == 'func':
245 # don't propagate 'instatus' crossing a function boundary
246 ta = _mergestatushints(x[2], instatus=False)
247 return (op, x[1], ta)
248 raise error.ProgrammingError('invalid operator %r' % op)
249
174 def analyze(x): 250 def analyze(x):
175 """Transform raw parsed tree to evaluatable tree which can be fed to 251 """Transform raw parsed tree to evaluatable tree which can be fed to
176 optimize() or getmatch() 252 optimize() or getmatch()
177 253
178 All pseudo operations should be mapped to real operations or functions 254 All pseudo operations should be mapped to real operations or functions
179 defined in methods or symbols table respectively. 255 defined in methods or symbols table respectively.
180 """ 256 """
181 return _analyze(x) 257 t = _analyze(x)
258 _h, t = _insertstatushints(t)
259 return _mergestatushints(t, instatus=False)
182 260
183 def _optimizeandops(op, ta, tb): 261 def _optimizeandops(op, ta, tb):
184 if tb is not None and tb[0] == 'not': 262 if tb is not None and tb[0] == 'not':
185 return ('minus', ta, tb[1]) 263 return ('minus', ta, tb[1])
186 return (op, ta, tb) 264 return (op, ta, tb)
203 def _optimize(x): 281 def _optimize(x):
204 if x is None: 282 if x is None:
205 return 0, x 283 return 0, x
206 284
207 op = x[0] 285 op = x[0]
286 if op == 'withstatus':
287 w, t = _optimize(x[1])
288 return w, (op, t, x[2])
208 if op in {'string', 'symbol'}: 289 if op in {'string', 'symbol'}:
209 return WEIGHT_CHECK_FILENAME, x 290 return WEIGHT_CHECK_FILENAME, x
210 if op == 'kindpat': 291 if op == 'kindpat':
211 w, t = _optimize(x[2]) 292 w, t = _optimize(x[2])
212 return w, (op, x[1], t) 293 return w, (op, x[1], t)