HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pathExpressionEval.h
Go to the documentation of this file.
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"
17 
18 #include "pxr/base/arch/regex.h"
20 
21 #include <string>
22 #include <type_traits>
23 #include <vector>
24 
26 
27 // fwd decl
28 template <class DomainType>
30 
31 // fwd decl
32 template <class DomainType>
36 
37 // fwd decl
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.
44 {
45 public:
46  friend SDF_API bool
49  SdfPathExpression const &expr,
51  void (SdfPathExpression::PathPattern const &)> translatePattern);
52 
53  /// Return true if this is the empty evalutator. The empty evaluator always
54  /// returns false from operator().
55  bool IsEmpty() const {
56  return _ops.empty();
57  }
58 
59  /// Return true if this is not the empty evaluator, false otherwise.
60  explicit operator bool() const {
61  return !IsEmpty();
62  }
63 
64 protected:
65  class _PatternImplBase;
66 
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 
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 
101  ExplicitName, // an explicit name (not a glob pattern).
102  Regex // a glob pattern (handled via regex).
103  };
104 
105  struct _Component {
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 
165  std::vector<_Component> _components;
166  std::vector<_Segment> _segments;
167  std::vector<std::string> _explicitNames;
168  std::vector<ArchRegex> _regexes;
169 
172  enum : uint8_t {
173  // The kind of objects this pattern is capable of matching.
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
186  SdfPredicateFunctionResult (bool /*skip*/)> invokePattern) const;
187 
188  enum _Op { EvalPattern, Not, Open, Close, Or, And };
189 
190  std::vector<_Op> _ops;
191 };
192 
193 /// \class SdfPathExpressionEval
194 ///
195 /// Objects of this class evaluate complete SdfPathExpressions. See
196 /// SdfMakePathExpressionEval() to create instances of this class, passing the
197 /// expression to evaluate and an SdfPredicateLibrary to evaluate any embedded
198 /// predicates.
199 ///
200 ///
201 template <class DomainType>
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 
249 public:
250  /// Make an SdfPathExpressionEval object to evaluate \p expr using \p lib to
251  /// link any embedded predicate expressions.
252  friend SdfPathExpressionEval
253  SdfMakePathExpressionEval<DomainType>(
254  SdfPathExpression const &expr,
256 
257  bool IsEmpty() const {
258  return _patternImpls.empty();
259  }
260 
261  /// Test \p objPath for a match with this expression.
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 
277  /// \class IncrementalSearcher
278  ///
279  /// This class implements stateful incremental search over DomainType
280  /// objects in depth-first order. See Next() for more info. This class is
281  /// copyable, and may be copied to parallelize searches over domain
282  /// subtrees, where one copy is invoked with a child, and the other with the
283  /// next sibling.
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 
303  /// Advance the search to the next \p objPath, and return the result of
304  /// evaluating the expression on it.
305  ///
306  /// The passed \p objPath must possibly succeed the previous object's
307  /// path in a valid depth-first ordering. That is, it must be a direct
308  /// child, a sibling, or the sibling of an ancestor. For example, the
309  /// following paths are in a valid order:
310  ///
311  /// /foo, /foo/bar, /foo/bar/baz, /foo/bar/qux, /oof, /oof/zab /oof/xuq
312  ///
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 
333  /// Reset this object's incremental search state so that a new round of
334  /// searching may begin.
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 
348  /// Create an IncrementalSearcher object, using \p pathToObject to map
349  /// DomainType instances to their paths.
350  template <class PathToObject>
351  IncrementalSearcher<std::decay_t<PathToObject>>
352  MakeIncrementalSearcher(PathToObject &&pathToObj) const {
353  return IncrementalSearcher<std::decay_t<PathToObject>>(
354  this, std::forward<PathToObject>(pathToObj));
355  }
356 
357 private:
358  std::vector<_PatternImpl> _patternImpls;
359 };
360 
361 /// Create an SdfPathExpressionEval object that can evaluate the complete
362 /// SdfPathExpression \p expr on DomainType instances, using \p lib, an
363 /// SdfPredicateLibrary<DomainType> to evaluate any embedded predicates.
364 ///
365 /// Note that \p expr must be "complete", meaning that
366 /// SdfPathExpression::IsComplete() must return true. If an evaluator cannot
367 /// succesfully be made, possibly because the passed \expr is not complete, or
368 /// if any embedded SdfPredicateExpression s cannot be successfully linked with
369 /// \p lib, or another reason, issue an error and return the empty
370 /// SdfPathExpressionEval object. See SdfPathExpressionEval::IsEmpty().
371 ///
372 template <class DomainType>
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 
397 
398 #endif // PXR_USD_SDF_PATH_EXPRESSION_EVAL_H
friend SDF_API bool Sdf_MakePathExpressionEvalImpl(Sdf_PathExpressionEvalBase &eval, SdfPathExpression const &expr, TfFunctionRef< void(SdfPathExpression::PathPattern const &)> translatePattern)
IncrementalSearcher(SdfPathExpressionEval const *eval, PathToObject const &p2o)
bool _IsBarePredicate(_Component const &comp) const
SDF_API SdfPredicateFunctionResult _CheckMatch(_Segment const &seg, _RunNthPredFn runNthPredicate, SdfPathVector::const_iterator pathIterBegin, SdfPathVector::const_iterator pathIterEnd, SdfPathVector::const_iterator &pathIterInOut) const
void skip(T &in, int n)
Definition: ImfXdr.h:613
SdfPredicateProgram< DomainType > SdfLinkPredicateExpression(SdfPredicateExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
GLsizei const GLchar *const * path
Definition: glcorearb.h:3341
SDF_API SdfPredicateFunctionResult _Match(SdfPath const &path, _RunNthPredFn runNthPredicate) const
OutGridT const XformOp bool bool
IncrementalSearcher< std::decay_t< PathToObject > > MakeIncrementalSearcher(PathToObject &&pathToObj) const
static SdfPredicateFunctionResult MakeConstant(bool value)
Create with value and 'ConstantOverDescendants'.
GLuint GLuint end
Definition: glcorearb.h:475
HUSD_API bool eval(VtValue &val, T &ret_val)
SDF_API size_t GetPathElementCount() const
Returns the number of path elements in this path.
Definition: path.h:273
GLushort pattern
Definition: glad.h:2583
auto search(const T &set, const V &val) -> std::pair< bool, decltype(std::begin(detail::smart_deref(set)))>
A search function.
Definition: CLI11.h:3170
SDF_API void _Init(SdfPathExpression::PathPattern const &pattern, TfFunctionRef< int(SdfPredicateExpression const &)> linkPredicate)
size_t _SegmentMinMatchElts(_Segment const &seg) const
#define SDF_API
Definition: api.h:23
GLsizeiptr size
Definition: glcorearb.h:664
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
IncrementalSearcher(SdfPathExpressionEval const *eval, PathToObject &&p2o)
SDF_API SdfPredicateFunctionResult _Next(_PatternIncrSearchState &searchState, SdfPath const &path, _RunNthPredFn runNthPredicate) const
SdfPathExpressionEval< DomainType > SdfMakePathExpressionEval(SdfPathExpression const &expr, SdfPredicateLibrary< DomainType > const &lib)
SDF_API SdfPredicateFunctionResult _EvalExpr(TfFunctionRef< SdfPredicateFunctionResult(bool)> invokePattern) const
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
SdfPredicateFunctionResult Match(SdfPath const &objPath, PathToObject const &pathToObj) const
Test objPath for a match with this expression.
SDF_API SdfPredicateFunctionResult _CheckExactMatch(_Segment const &seg, _RunNthPredFn runNthPredicate, SdfPathVector::const_iterator pathIterEnd, SdfPathVector::const_iterator &pathIterInOut) const
TfFunctionRef< SdfPredicateFunctionResult(int, SdfPath const &)> _RunNthPredFn
SdfPredicateFunctionResult Next(SdfPath const &objPath)
state
Definition: core.h:2289
enum Sdf_PathExpressionEvalBase::_PatternImplBase::@588 _matchObjType