HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
variableExpression.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_VARIABLE_EXPRESSION
8 #define PXR_USD_SDF_VARIABLE_EXPRESSION
9 
10 /// \file sdf/variableExpression.h
11 
12 #include "pxr/pxr.h"
13 #include "pxr/usd/sdf/api.h"
14 
15 #include "pxr/base/vt/array.h"
16 #include "pxr/base/vt/dictionary.h"
17 #include "pxr/base/vt/value.h"
18 
19 #include <memory>
20 #include <string>
21 #include <unordered_set>
22 #include <vector>
23 
25 
26 namespace Sdf_VariableExpressionImpl {
27  class Node;
28 }
29 
30 /// \class SdfVariableExpression
31 ///
32 /// Class responsible for parsing and evaluating variable expressions.
33 ///
34 /// Variable expressions are written in a custom language and
35 /// represented in scene description as a string surrounded by backticks (`).
36 /// These expressions may refer to "expression variables", which are key-value
37 /// pairs provided by clients. For example, when evaluating an expression like:
38 ///
39 /// \code
40 /// `"a_${NAME}_string"`
41 /// \endcode
42 ///
43 /// The "${NAME}" portion of the string with the value of expression variable
44 /// "NAME".
45 ///
46 /// Expression variables may be any of these supported types:
47 ///
48 /// - std::string
49 /// - int64_t (int is accepted but coerced to int64_t)
50 /// - bool
51 /// - VtArrays containing any of the above types.
52 /// - None (represented by an empty VtValue)
53 ///
54 /// Expression variables are typically authored in scene description as layer
55 /// metadata under the 'expressionVariables' field. Higher levels of the system
56 /// (e.g., composition) are responsible for examining fields that support
57 /// variable expressions, evaluating them with the appropriate variables (via
58 /// this class) and consuming the results.
59 ///
60 /// See \ref Sdf_Page_VariableExpressions "Variable Expressions"
61 /// or more information on the expression language and areas of the system
62 /// where expressions may be used.
64 {
65 public:
66  /// Construct using the expression \p expr. If the expression cannot be
67  /// parsed, this object represents an invalid expression. Parsing errors
68  /// will be accessible via GetErrors.
69  SDF_API
70  explicit SdfVariableExpression(const std::string& expr);
71 
72  /// \overload
73  SDF_API
74  explicit SdfVariableExpression(std::string&& expr);
75 
76  /// Construct an object representing an invalid expression.
77  SDF_API
79 
80  SDF_API
82 
83  /// Returns true if \p s is a variable expression, false otherwise.
84  /// A variable expression is a string surrounded by backticks (`).
85  ///
86  /// A return value of true does not guarantee that \p s is a valid
87  /// expression. This function is meant to be used as an initial check
88  /// to determine if a string should be considered as an expression.
89  SDF_API
90  static bool IsExpression(const std::string& s);
91 
92  /// Returns true if \p value holds a type that is supported by
93  /// variable expressions, false otherwise. If this function returns
94  /// true, \p value may be used for an expression variable supplied to
95  /// the Evaluate function. \p value may also be authored into the
96  /// 'expressionVariables' dictionary, unless it is an empty VtValue
97  /// representing the None value. See class documentation for list of
98  /// supported types.
99  SDF_API
100  static bool IsValidVariableType(const VtValue& value);
101 
102  /// Returns true if this object represents a valid expression, false
103  /// if it represents an invalid expression.
104  ///
105  /// A return value of true does not mean that evaluation of this
106  /// expression is guaranteed to succeed. For example, an expression may
107  /// refer to a variable whose value is an invalid expression.
108  /// Errors like this can only be discovered by calling Evaluate.
109  SDF_API
110  explicit operator bool() const;
111 
112  /// Returns the expression string used to construct this object.
113  SDF_API
114  const std::string& GetString() const;
115 
116  /// Returns a list of errors encountered when parsing this expression.
117  ///
118  /// If the expression was parsed successfully, this list will be empty.
119  /// However, additional errors may be encountered when evaluating the e
120  /// expression.
121  SDF_API
122  const std::vector<std::string>& GetErrors() const;
123 
124  /// \name Evaluation
125  /// @{
126 
127  /// \class EmptyList
128  /// A result value representing an empty list.
129  class EmptyList { };
130 
131  /// \class Result
132  class Result
133  {
134  public:
135  /// The result of evaluating the expression. This value may be
136  /// empty if the expression yielded no value. It may also be empty
137  /// if errors occurred during evaluation. In this case, the errors
138  /// field will be populated with error messages.
139  ///
140  /// If the value is not empty, it will contain one of the supported
141  /// types listed in the class documentation.
143 
144  /// Errors encountered while evaluating the expression.
145  std::vector<std::string> errors;
146 
147  /// Set of variables that were used while evaluating
148  /// the expression. For example, for an expression like
149  /// `"example_${VAR}_expression"`, this set will contain "VAR".
150  ///
151  /// This set will also contain variables from subexpressions.
152  /// In the above example, if the value of "VAR" was another
153  /// expression like `"sub_${SUBVAR}_expression"`, this set will
154  /// contain both "VAR" and "SUBVAR".
155  std::unordered_set<std::string> usedVariables;
156  };
157 
158  /// Evaluates this expression using the variables in
159  /// \p variables and returns a Result object with the final
160  /// value. If an error occurs during evaluation, the value field
161  /// in the Result object will be an empty VtValue and error messages
162  /// will be added to the errors field.
163  ///
164  /// If the expression evaluates to an empty list, the value field
165  /// in the Result object will contain an EmptyList object instead
166  /// of an empty VtArray<T>, as the expression language does not
167  /// provide syntax for specifying the expected element types in
168  /// an empty list.
169  ///
170  /// If this object represents an invalid expression, calling this
171  /// function will return a Result object with an empty value and the
172  /// errors from GetErrors().
173  ///
174  /// If any values in \p variables used by this expression
175  /// are themselves expressions, they will be parsed and evaluated.
176  /// If an error occurs while evaluating any of these subexpressions,
177  /// evaluation of this expression fails and the encountered errors
178  /// will be added in the Result's list of errors.
179  SDF_API
180  Result Evaluate(const VtDictionary& variables) const;
181 
182  /// Evaluates this expression using the variables in
183  /// \p variables and returns a Result object with the final
184  /// value.
185  ///
186  /// This is a convenience function that calls Evaluate and ensures that
187  /// the value in the Result object is either an empty VtValue or is
188  /// holding the specified ResultType. If this is not the case, the
189  /// Result value will be set to an empty VtValue an error message
190  /// indicating the unexpected type will be added to the Result's error
191  /// list. Otherwise, the Result will be returned as-is.
192  ///
193  /// If the expression evaluates to an empty list and the ResultType
194  /// is a VtArray<T>, the value in the Result object will be an empty
195  /// VtArray<T>. This differs from Evaluate, which would return an
196  /// untyped EmptyList object instead.
197  ///
198  /// ResultType must be one of the supported types listed in the
199  /// class documentation.
200  template <class ResultType>
201  Result EvaluateTyped(const VtDictionary& variables) const
202  {
203  Result r = Evaluate(variables);
204 
206  r.value = VtValue(ResultType());
207  }
208  else if (!r.value.IsEmpty() && !r.value.IsHolding<ResultType>()) {
209  r.errors.push_back(
210  _FormatUnexpectedTypeError(r.value, VtValue(ResultType())));
211  r.value = VtValue();
212  }
213  return r;
214  }
215 
216  /// @}
217 
218  /// \name Building Expressions
219  /// Utilities for programatically building a variable expression.
220  /// These functions can be chained together to create complex
221  /// expressions. For example:
222  ///
223  /// \code
224  /// const SdfVariableExpression containsExpr =
225  /// SdfVariableExpression::MakeFunction(
226  /// "contains",
227  /// SdfVariableExpression::MakeList(
228  /// SdfVariableExpression::MakeLiteral("foo"),
229  /// SdfVariableExpression::MakeLiteral("bar")),
230  /// SdfVariableExpression::MakeVariable("VAR"));
231  /// \endcode
232  ///
233  /// This yields the expression `contains(["foo", "bar"], ${VAR})`.
234  ///
235  /// Note that these functions may yield invalid expressions that
236  /// cannot be evaluated. For example, calling MakeFunction with
237  /// an unrecognized function name will produce an SdfVariableExpression
238  /// whose bool operator returns false. However, calling GetString on
239  /// the returned SdfVariableExpression will still return the
240  /// expression string for inspection.
241  ///
242  /// @{
243 
244  /// \class Builder
245  /// Helper class for storing intermediate results when building
246  /// a variable expression.
247  class Builder
248  {
249  public:
250  SDF_API operator SdfVariableExpression() const;
251 
252  private:
253  friend class SdfVariableExpression;
254  Builder(std::string&& expr) : _expr(std::move(expr)) { }
255  std::string _expr;
256  };
257 
258  /// \class FunctionBuilder
259  /// Helper class for storing intermediate results when building
260  /// a function variable expression.
262  {
263  public:
264  /// Add an expression as an argument to the function call.
265  /// \see MakeFunction
266  template <class Argument>
267  FunctionBuilder& AddArgument(Argument&& arg);
268 
269  SDF_API operator SdfVariableExpression() const;
270  SDF_API operator Builder() const &;
271  SDF_API operator Builder() &&;
272 
273  private:
274  friend class SdfVariableExpression;
275  FunctionBuilder(const std::string& name) : _expr(name + '(') { }
276  std::string _expr;
277  };
278 
279  /// \class ListBuilder
280  /// Helper class for storing intermediate results when building
281  /// a list variable expression.
283  {
284  public:
285  /// Add an expression as an element to the list.
286  /// \see MakeList
287  template <class Element>
288  ListBuilder& AddElement(Element&& elem);
289 
290  /// Add values in \p values as literal expressions to the list.
291  /// \see MakeList
292  template <class T>
293  ListBuilder& AddLiteralValues(const std::vector<T>& values);
294 
295  SDF_API operator SdfVariableExpression() const;
296  SDF_API operator Builder() const &;
297  SDF_API operator Builder() &&;
298 
299  private:
300  friend class SdfVariableExpression;
301  ListBuilder() : _expr("[") { }
302 
303  std::string _expr;
304  };
305 
306  /// Create a function expression that calls the function named \p fnName
307  /// with \p fnArgs as arguments, i.e. `fnName(fnArgs1, fnArgs2, ...)`.
308  ///
309  /// \p fnArgs must be other SdfVariableExpression objects or the result
310  /// of other expression builder functions.
311  template <class... Arguments>
312  static FunctionBuilder
313  MakeFunction(const std::string& fnName, Arguments&&... fnArgs)
314  {
315  FunctionBuilder b(fnName);
316  (b.AddArgument(std::forward<Arguments>(fnArgs)), ...);
317  return b;
318  }
319 
320  /// Create a list expression with \p listElems as elements, i.e.
321  /// `[listElems1, listElems2, ...]`.
322  ///
323  /// \p elems must be other SdfVariableExpression objects or the result
324  /// of other expression builder functions.
325  template <class... Elements>
326  static ListBuilder
327  MakeList(Elements&&... elems)
328  {
329  ListBuilder b;
330  (b.AddElement(std::forward<Elements>(elems)), ...);
331  return b;
332  }
333 
334  /// Create a list expression with the values in \p values as literal
335  /// elements.
336  ///
337  /// \p values must hold types that can be represented by literal
338  /// expressions, i.e. a type for which MakeLiteral is defined.
339  template <class T>
340  static ListBuilder
341  MakeListOfLiterals(const std::vector<T>& values)
342  {
343  return ListBuilder().AddLiteralValues(values);
344  }
345 
346  /// Create a literal expression for \p value.
347  SDF_API static Builder MakeLiteral(int64_t value);
348  SDF_API static Builder MakeLiteral(bool value);
349  SDF_API static Builder MakeLiteral(const std::string& value);
350  SDF_API static Builder MakeLiteral(const char* value);
351 
352  /// Create a "None" literal expression.
353  SDF_API static Builder MakeNone();
354 
355  /// Create a variable reference expression for the variable named
356  /// \p name, i.e. `${name}`.
357  SDF_API static Builder MakeVariable(const std::string& name);
358 
359  /// @}
360 
361 private:
362  SDF_API
363  static std::string
364  _FormatUnexpectedTypeError(const VtValue& got, const VtValue& expected);
365 
366  SDF_API
367  static void
368  _AppendExpression(
369  std::string* expr, const SdfVariableExpression& arg, bool first);
370 
371  SDF_API
372  static void
373  _AppendBuilder(std::string* expr, const Builder& b, bool first);
374 
375  template <class Argument>
376  static void
377  _Append(std::string* expr, Argument&& arg, bool first)
378  {
379  // Avoid implicitly converting arg to an SdfVariableExpression
380  // since that would incur unnecessary parsing costs.
381  if constexpr (std::is_same_v<
382  std::decay_t<Argument>, SdfVariableExpression>) {
383  _AppendExpression(expr, std::forward<Argument>(arg), first);
384  }
385  else {
386  _AppendBuilder(expr, std::forward<Argument>(arg), first);
387  }
388  }
389 
390  std::vector<std::string> _errors;
391  std::shared_ptr<Sdf_VariableExpressionImpl::Node> _expression;
392  std::string _expressionStr;
393 };
394 
395 inline bool
399 {
400  return true;
401 }
402 
403 inline bool
407 {
408  return false;
409 }
410 
411 template <class Argument>
414 {
415  SdfVariableExpression::_Append(
416  &_expr, std::forward<Argument>(arg),
417  /* first = */ *_expr.rbegin() == '(');
418  return *this;
419 }
420 
421 template <class Element>
424 {
425  SdfVariableExpression::_Append(
426  &_expr, std::forward<Element>(arg),
427  /* first = */ *_expr.rbegin() == '[');
428  return *this;
429 }
430 
431 template <class T>
434  const std::vector<T>& values)
435 {
436  for (const T& v : values) {
438  }
439  return *this;
440 }
441 
443 
444 #endif
GLint first
Definition: glcorearb.h:405
ListBuilder & AddLiteralValues(const std::vector< T > &values)
SDF_API SdfVariableExpression()
Construct an object representing an invalid expression.
SDF_API Result Evaluate(const VtDictionary &variables) const
Definition: Node.h:52
std::unordered_set< std::string > usedVariables
const GLdouble * v
Definition: glcorearb.h:837
SDF_API ~SdfVariableExpression()
static SDF_API bool IsExpression(const std::string &s)
#define PXR_NAMESPACE_OPEN_SCOPE
Definition: pxr.h:73
GLsizei const GLfloat * value
Definition: glcorearb.h:824
static ListBuilder MakeList(Elements &&...elems)
static FunctionBuilder MakeFunction(const std::string &fnName, Arguments &&...fnArgs)
GLdouble s
Definition: glad.h:3009
static SDF_API Builder MakeLiteral(int64_t value)
Create a literal expression for value.
bool IsEmpty() const
Returns true iff this value is empty.
Definition: value.h:1227
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1859
OutGridT const XformOp bool bool
static ListBuilder MakeListOfLiterals(const std::vector< T > &values)
SDF_API const std::vector< std::string > & GetErrors() const
bool operator!=(const Mat3< T0 > &m0, const Mat3< T1 > &m1)
Inequality operator, does exact floating point comparisons.
Definition: Mat3.h:556
static SDF_API bool IsValidVariableType(const VtValue &value)
GLuint const GLchar * name
Definition: glcorearb.h:786
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
FunctionBuilder & AddArgument(Argument &&arg)
static SDF_API Builder MakeNone()
Create a "None" literal expression.
A trait to detect instantiations of VtArray, specialized in array.h.
Definition: traits.h:22
#define SDF_API
Definition: api.h:23
std::vector< std::string > errors
Errors encountered while evaluating the expression.
GLenum GLsizei GLsizei GLint * values
Definition: glcorearb.h:1602
bool IsHolding() const
Definition: value.h:1002
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
static SDF_API Builder MakeVariable(const std::string &name)
GLboolean r
Definition: glcorearb.h:1222
ListBuilder & AddElement(Element &&elem)
SDF_API const std::string & GetString() const
Returns the expression string used to construct this object.
Definition: value.h:89
bool operator==(const Mat3< T0 > &m0, const Mat3< T1 > &m1)
Equality operator, does exact floating point comparisons.
Definition: Mat3.h:542
Result EvaluateTyped(const VtDictionary &variables) const