Loading...
Searching...
No Matches
pathExpressionEval.h
1//
2// Copyright 2023 Pixar
3//
4// Licensed under the terms set forth in the LICENSE.txt file available at
5// https://openusd.org/license.
6//
7#ifndef PXR_USD_SDF_PATH_EXPRESSION_EVAL_H
8#define PXR_USD_SDF_PATH_EXPRESSION_EVAL_H
9
10#include "pxr/pxr.h"
11#include "pxr/usd/sdf/api.h"
12#include "pxr/usd/sdf/path.h"
13#include "pxr/usd/sdf/pathExpression.h"
14#include "pxr/usd/sdf/predicateExpression.h"
15#include "pxr/usd/sdf/predicateLibrary.h"
16#include "pxr/usd/sdf/predicateProgram.h"
17
18#include "pxr/base/arch/regex.h"
19#include "pxr/base/tf/functionRef.h"
20
21#include <string>
22#include <type_traits>
23#include <vector>
24
25PXR_NAMESPACE_OPEN_SCOPE
26
27// fwd decl
28template <class DomainType>
30
31// fwd decl
32template <class DomainType>
36
37// fwd decl
38class Sdf_PathExpressionEvalBase;
39
40// Helper base class for SdfPathExpressionEval. This factors out as much
41// template-parameter independent code as possible to reduce bloat & compile
42// times.
43class Sdf_PathExpressionEvalBase
44{
45public:
46 friend SDF_API bool
47 Sdf_MakePathExpressionEvalImpl(
48 Sdf_PathExpressionEvalBase &eval,
49 SdfPathExpression const &expr,
51 void (SdfPathExpression::PathPattern const &)> translatePattern);
52
55 bool IsEmpty() const {
56 return _ops.empty();
57 }
58
60 explicit operator bool() const {
61 return !IsEmpty();
62 }
63
64protected:
65 class _PatternImplBase;
66
67 class _PatternIncrSearchState {
68 friend class _PatternImplBase;
69 public:
70 SDF_API void Pop(int newDepth);
71 private:
72 std::vector<int> _segmentMatchDepths;
73 int _constantDepth = -1;
74 bool _constantValue = false;
75 };
76
77 class _PatternImplBase {
78 protected:
79 using _RunNthPredFn =
81
82 // This is not a constructor because the subclass wants to invoke this
83 // from its ctor, TfFunctionRef currently requires an lvalue, which is
84 // hard to conjure in a ctor initializer list.
85 SDF_API
86 void _Init(SdfPathExpression::PathPattern const &pattern,
88 int (SdfPredicateExpression const &)> linkPredicate);
89
90 SDF_API
92 _Match(SdfPath const &path, _RunNthPredFn runNthPredicate) const;
93
94 SDF_API
96 _Next(_PatternIncrSearchState &searchState,
97 SdfPath const &path, _RunNthPredFn runNthPredicate) const;
98
99
100 enum _ComponentType {
101 ExplicitName, // an explicit name (not a glob pattern).
102 Regex // a glob pattern (handled via regex).
103 };
104
105 struct _Component {
106 _ComponentType type;
107 int patternIndex; // into either _explicitNames or _regexes
108 int predicateIndex; // into _predicates or -1 if no predicate.
109 };
110
111 struct _Segment {
112 // A _Segment is a half-open interval [begin, end) in _components.
113 bool IsEmpty() const { return begin == end; }
114 bool StartsAt(size_t idx) const { return begin == idx; }
115 bool EndsAt(size_t idx) const { return end == idx; }
116 size_t GetSize() const { return end - begin; }
117 size_t begin, end;
118 };
119
120 bool _IsBarePredicate(_Component const &comp) const {
121 // An empty explicit name component with a predicate is a "bare
122 // predicate".
123 return comp.type == ExplicitName &&
124 _explicitNames[comp.patternIndex].empty() &&
125 comp.predicateIndex != -1;
126 }
127
128 size_t _SegmentMinMatchElts(_Segment const &seg) const {
129 // If a segment starts with a bare predicate, it may match the bare
130 // predicate to the *prior* path element, so it requires one fewer
131 // path element to match than the number of segment components.
132 return seg.GetSize() - (_IsBarePredicate(
133 _components[seg.begin]) ? 1 : 0);
134 }
135
136 // Check if \p seg matches at exactly \p pathIterInOut. Update \p
137 // pathIterInOut to the position past this match if there is a match and
138 // return a truthy result. Otherwise leave \p pathIterInOut untouched
139 // and return a falsey result.
140 SDF_API
142 _CheckExactMatch(_Segment const &seg,
143 _RunNthPredFn runNthPredicate,
144 SdfPathVector::const_iterator pathIterEnd,
145 SdfPathVector::const_iterator &pathIterInOut) const;
146
147 // Check if \p seg matches at \p pathIterInOut, or at
148 // `std::prev(pathIterInOut)` if \p seg starts with a bare predicate
149 // (like //{predicate}). Update \p pathIterInOut to the position past
150 // the match if there is a match and return a truthy result. Otherwise
151 // leave \p pathIterInOut untouched and return a falsey result. The key
152 // difference between _CheckMatch() and _CheckExactMatch() is that if \p
153 // seg begins with a bare predicate, it can potentially match at
154 // `std::prev(pathIterInOut)`.
155 SDF_API
157 _CheckMatch(_Segment const &seg,
158 _RunNthPredFn runNthPredicate,
159 SdfPathVector::const_iterator pathIterBegin,
160 SdfPathVector::const_iterator pathIterEnd,
161 SdfPathVector::const_iterator &pathIterInOut) const;
162
163
164 SdfPath _prefix;
165 std::vector<_Component> _components;
166 std::vector<_Segment> _segments;
167 std::vector<std::string> _explicitNames;
168 std::vector<ArchRegex> _regexes;
169
170 bool _stretchBegin;
171 bool _stretchEnd;
172 enum : uint8_t {
173 // The kind of objects this pattern is capable of matching.
174 _MatchPrimOrProp, _MatchPrimOnly, _MatchPropOnly
175 } _matchObjType;
176 };
177
178
179 // The passed \p invokePattern function must do two things: 1, if \p skip is
180 // false, test the current pattern for a match (otherwise skip it) and 2,
181 // advance to be ready to test the next pattern for a match on the next call
182 // to \p invokePattern.
183 SDF_API
185 _EvalExpr(TfFunctionRef<
186 SdfPredicateFunctionResult (bool /*skip*/)> invokePattern) const;
187
188 enum _Op { EvalPattern, Not, Open, Close, Or, And };
189
190 std::vector<_Op> _ops;
191};
192
201template <class DomainType>
202class SdfPathExpressionEval : public Sdf_PathExpressionEvalBase
203{
204 // This object implements matching against a single path pattern.
205 class _PatternImpl : public _PatternImplBase {
206 public:
207 _PatternImpl() = default;
208
209 _PatternImpl(SdfPathExpression::PathPattern const &pattern,
210 SdfPredicateLibrary<DomainType> const &predLib) {
211 auto linkPredicate =
212 [this, &predLib](SdfPredicateExpression const &predExpr) {
213 _predicates.push_back(
214 SdfLinkPredicateExpression(predExpr, predLib));
215 return _predicates.size()-1;
216 };
217 _Init(pattern, linkPredicate);
218 }
219
220 // Check objPath for a match against this pattern.
221 template <class PathToObject>
223 Match(SdfPath const &objPath,
224 PathToObject const &pathToObj) const {
225 auto runNthPredicate =
226 [this, &pathToObj](int i, SdfPath const &path) {
227 return _predicates[i](pathToObj(path));
228 };
229 return _Match(objPath, runNthPredicate);
230 }
231
232 // Perform the next incremental search step against this pattern.
233 template <class PathToObject>
235 Next(SdfPath const &objPath,
236 _PatternIncrSearchState &search,
237 PathToObject const &pathToObj) const {
238 auto runNthPredicate =
239 [this, &pathToObj](int i, SdfPath const &path) {
240 return _predicates[i](pathToObj(path));
241 };
242 return _Next(search, objPath, runNthPredicate);
243 }
244
245 private:
246 std::vector<SdfPredicateProgram<DomainType>> _predicates;
247 };
248
249public:
253 SdfMakePathExpressionEval<DomainType>(
254 SdfPathExpression const &expr,
256
257 bool IsEmpty() const {
258 return _patternImpls.empty();
259 }
260
262 template <class PathToObject>
264 Match(SdfPath const &objPath,
265 PathToObject const &pathToObj) const {
266 if (IsEmpty()) {
268 }
269 auto patternImplIter = _patternImpls.cbegin();
270 auto evalPattern = [&](bool skip) {
271 return skip ? (++patternImplIter, SdfPredicateFunctionResult()) :
272 (*patternImplIter++).Match(objPath, pathToObj);
273 };
274 return _EvalExpr(evalPattern);
275 }
276
284 template <class PathToObject>
286 public:
287 IncrementalSearcher() : _eval(nullptr), _lastPathDepth(0) {}
288
290 PathToObject const &p2o)
291 : _eval(eval)
292 , _incrSearchStates(_eval->_patternImpls.size())
293 , _pathToObj(p2o)
294 , _lastPathDepth(0) {}
295
297 PathToObject &&p2o)
298 : _eval(eval)
299 , _incrSearchStates(_eval->_patternImpls.size())
300 , _pathToObj(std::move(p2o))
301 , _lastPathDepth(0) {}
302
314 Next(SdfPath const &objPath) {
315 auto patternImplIter = _eval->_patternImpls.begin();
316 auto stateIter = _incrSearchStates.begin();
317 const int newDepth = objPath.GetPathElementCount();
318 const bool pop = newDepth <= _lastPathDepth;
319 auto patternStateNext = [&](bool skip) {
320 if (pop) {
321 stateIter->Pop(newDepth);
322 }
323 auto const &patternImpl = *patternImplIter++;
324 auto &state = *stateIter++;
325 return skip
327 : patternImpl.Next(objPath, state, _pathToObj);
328 };
329 _lastPathDepth = newDepth;
330 return _eval->_EvalExpr(patternStateNext);
331 }
332
335 void Reset() {
336 *this = IncrementalSearcher { _eval, std::move(_pathToObj) };
337 }
338
339 private:
340 SdfPathExpressionEval const *_eval;
341 std::vector<_PatternIncrSearchState> _incrSearchStates;
342
343 PathToObject _pathToObj;
344
345 int _lastPathDepth;
346 };
347
350 template <class PathToObject>
351 IncrementalSearcher<std::decay_t<PathToObject>>
352 MakeIncrementalSearcher(PathToObject &&pathToObj) const {
354 this, std::forward<PathToObject>(pathToObj));
355 }
356
357private:
358 std::vector<_PatternImpl> _patternImpls;
359};
360
372template <class DomainType>
374SdfMakePathExpressionEval(SdfPathExpression const &expr,
376{
377 using Expr = SdfPathExpression;
379
380 Eval eval;
381
382 auto translatePattern = [&](Expr::PathPattern const &pattern) {
383 // Add a _PatternImpl object that tests a DomainType object against
384 // pattern.
385 eval._patternImpls.emplace_back(pattern, lib);
386 eval._ops.push_back(Eval::EvalPattern);
387 };
388
389 if (!Sdf_MakePathExpressionEvalImpl(eval, expr, translatePattern)) {
390 eval = {};
391 }
392
393 return eval;
394}
395
396PXR_NAMESPACE_CLOSE_SCOPE
397
398#endif // PXR_USD_SDF_PATH_EXPRESSION_EVAL_H
This class implements stateful incremental search over DomainType objects in depth-first order.
void Reset()
Reset this object's incremental search state so that a new round of searching may begin.
SdfPredicateFunctionResult Next(SdfPath const &objPath)
Advance the search to the next objPath, and return the result of evaluating the expression on it.
Objects of this class evaluate complete SdfPathExpressions.
friend SdfPathExpressionEval SdfMakePathExpressionEval(SdfPathExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
Make an SdfPathExpressionEval object to evaluate expr using lib to link any embedded predicate expres...
IncrementalSearcher< std::decay_t< PathToObject > > MakeIncrementalSearcher(PathToObject &&pathToObj) const
Create an IncrementalSearcher object, using pathToObject to map DomainType instances to their paths.
SdfPredicateFunctionResult Match(SdfPath const &objPath, PathToObject const &pathToObj) const
Test objPath for a match with this expression.
Objects of this class represent a logical expression syntax tree consisting of SdfPathPattern s,...
A path value used to locate objects in layers or scenegraphs.
Definition: path.h:274
SDF_API size_t GetPathElementCount() const
Returns the number of path elements in this path.
Objects of this class represent SdfPath matching patterns, consisting of an SdfPath prefix followed b...
Definition: pathPattern.h:31
Represents a logical expression syntax tree consisting of predicate function calls joined by the logi...
Represents the result of a predicate function: a pair of the boolean result and a Constancy token ind...
static SdfPredicateFunctionResult MakeConstant(bool value)
Create with value and 'ConstantOverDescendants'.
Represents a library of predicate functions for use with SdfPredicateExpression.
This class provides a non-owning reference to a type-erased callable object with a specified signatur...
Definition: functionRef.h:19