HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
predicateExpression.h
Go to the documentation of this file.
1 //
2 // Copyright 2023 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_USD_SDF_PREDICATE_EXPRESSION_H
25 #define PXR_USD_SDF_PREDICATE_EXPRESSION_H
26 
27 #include "pxr/pxr.h"
28 #include "pxr/usd/sdf/api.h"
29 #include "pxr/base/vt/value.h"
30 
31 #include <string>
32 #include <vector>
33 
35 
36 /// \class SdfPredicateExpression
37 ///
38 /// Represents a logical expression syntax tree consisting of predicate function
39 /// calls joined by the logical operators 'and', 'or', 'not', and an implied-and
40 /// operator that represents two subexpressions joined by only whitespace.
41 ///
42 /// An SdfPredicateExpression can be constructed with a string, which will parse
43 /// an expression. The syntax for an expression is as follows:
44 ///
45 /// The fundamental building blocks are calls to predicate functions. There are
46 /// three syntaxes for function calls.
47 ///
48 /// \li Bare call: just a function name: `isDefined`
49 /// \li Colon call: name, colon, positional arguments: `isa:mammal,bird`
50 /// \li Paren call: name and parenthesized positional and keyword args:
51 /// `isClose(1.23, tolerance=0.01)`
52 ///
53 /// Colon call arguments are all positional and must be separated by commas with
54 /// no spaces between arguments. In paren calls, positional arguments must
55 /// precede keyword arguments, and whitespace is allowed between arguments.
56 ///
57 /// The string parser supports argument values of the following types:
58 /// double-quoted "strings", unquoted strings, integers, floating-point numbers,
59 /// and boolean values 'true' and 'false'.
60 ///
61 /// The unary operator 'not' may appear preceding a function call, or a
62 /// subexpresion enclosed in parentheses. The binary operators 'and' and 'or'
63 /// may appear between subexpressions. If subexpressions appear adjacent to each
64 /// other (other than possible whitespace), this is considered an implied 'and'
65 /// operator.
66 ///
67 /// Operator precedence in order from highest to lowest is: 'not',
68 /// <implied-and>, 'and', 'or'.
69 ///
70 /// Here are some examples of valid predicate expression syntax:
71 ///
72 /// \li `foo` (call "foo" with no arguments)
73 /// \li `foo bar` (implicit 'and' of "foo" and "bar")
74 /// \li `foo not bar` (implicit 'and' of "foo" and "not bar")
75 /// \li `color:red (shiny or matte)`
76 /// \li `animal or mineral or vegetable`
77 /// \li `(mammal or bird) and (tame or small)`
78 /// \li `isClose(100, tolerance=3.0) or negative`
79 ///
81 {
82 public:
83 
84  /// \class FnArg
85  ///
86  /// Represents a function argument name and value. Positional arguments
87  /// have empty names.
88  struct FnArg {
89  static FnArg Positional(VtValue const &val) {
90  return { std::string(), val };
91  }
92  static FnArg Keyword(std::string const &name, VtValue const &val) {
93  return { name, val };
94  }
97 
98  friend bool operator==(FnArg const &l, FnArg const &r) {
99  return std::tie(l.argName, l.value) == std::tie(r.argName, r.value);
100  }
101  friend bool operator!=(FnArg const &l, FnArg const &r) {
102  return std::tie(l.argName, l.value) != std::tie(r.argName, r.value);
103  }
104  };
105 
106  /// \class FnCall
107  ///
108  /// Represents a function call in an expression with calling style, function
109  /// name, and arguments.
110  struct FnCall {
111  enum Kind {
112  BareCall, ///< no-arg call like 'active'
113  ColonCall, ///< colon-separated pos args, like 'isa:Imageable'
114  ParenCall ///< paren/comma & pos/kw args like 'foo(23, bar=baz)'
115  };
116 
119  std::vector<FnArg> args;
120  };
121 
122  /// Construct the empty expression whose bool-operator returns false.
123  SdfPredicateExpression() = default;
124 
125  /// Copy construct from another expression.
127 
128  /// Move construct from another expression.
130 
131  /// Construct an expression by parsing \p expr. If provided, \p context
132  /// appears in a parse error, if one is generated. See GetParseError().
133  /// See the class documentation for details on expression syntax.
134  SDF_API
135  explicit SdfPredicateExpression(std::string const &expr,
136  std::string const &context = {});
137 
138  /// Copy assign from another expression.
140  operator=(SdfPredicateExpression const &) = default;
141 
142  /// Move assign from another expression.
144  operator=(SdfPredicateExpression &&) = default;
145 
146  /// Enumerant describing a subexpression operation.
147  enum Op { Call, Not, ImpliedAnd, And, Or };
148 
149  /// Produce a new expression by prepending the 'not' operator onto \p right.
150  SDF_API
153 
154  /// Produce a new expression by combining \p left and \p right with the
155  /// operator \p op. The \p op must be one of ImpliedAnd, And, or Or.
156  SDF_API
158  MakeOp(Op op,
161 
162  /// Produce a new expression containing just a the function call \p call.
163  SDF_API
165  MakeCall(FnCall &&call);
166 
167  /// Walk this expression's syntax tree in depth-first order, calling \p call
168  /// with the current function call when a function call is encountered, and
169  /// calling \p logic multiple times for each logical operation encountered.
170  /// When calling \p logic, the logical operation is passed as the \p Op
171  /// parameter, and an integer indicating "where" we are in the set of
172  /// operands is passed as the int parameter. For a 'not', call \p
173  /// logic(Op=Not, int=0) to start, then after the subexpression that the
174  /// 'not' applies to is walked, call \p logic(Op=Not, int=1). For the
175  /// binary operators like 'and' and 'or', call \p logic(Op, 0) before the
176  /// first argument, then \p logic(Op, 1) after the first subexpression, then
177  /// \p logic(Op, 2) after the second subexpression. For a concrete example,
178  /// consider the following expression:
179  ///
180  /// (foo or bar) and not baz
181  ///
182  /// The sequence of calls from Walk() will be:
183  ///
184  /// logic(And, 0)
185  /// logic(Or, 0)
186  /// call("foo")
187  /// logic(Or, 1)
188  /// call("bar")
189  /// logic(Or, 2)
190  /// logic(And, 1)
191  /// logic(Not, 0)
192  /// call("baz")
193  /// logic(Not, 1)
194  /// logic(And, 2)
195  ///
196  SDF_API
197  void Walk(TfFunctionRef<void (Op, int)> logic,
198  TfFunctionRef<void (FnCall const &)> call) const;
199 
200  /// Equivalent to Walk(), except that the \p logic function is called with a
201  /// const reference to the current Op stack instead of just the top of it.
202  /// The top of the Op stack is the vector's back. This is useful in case
203  /// the processing code needs to understand the context in which an Op
204  /// appears.
205  SDF_API
206  void WalkWithOpStack(
207  TfFunctionRef<void (std::vector<std::pair<Op, int>> const &)> logic,
208  TfFunctionRef<void (FnCall const &)> call) const;
209 
210  /// Return a text representation of this expression that parses to the same
211  /// expression.
212  SDF_API
213  std::string GetText() const;
214 
215  /// Return true if this is the empty expression; i.e. default-constructed or
216  /// constructed from a string with invalid syntax.
217  bool IsEmpty() const {
218  return _ops.empty();
219  }
220 
221  /// Return true if this expression contains any operations, false otherwise.
222  explicit operator bool() const {
223  return !IsEmpty();
224  }
225 
226  /// Return parsing errors as a string if this function was constructed from
227  /// a string and parse errors were encountered.
229  return _parseError;
230  }
231 
232  /// Return parsing errors as a string if this function was constructed from
233  /// a string and parse errors were encountered.
235  return _parseError;
236  }
237 
238 private:
239  // The expression is represented in function-call style, but *in reverse* to
240  // facilitate efficient assembly. For example, an expression like "a and b"
241  // would be represented as { Call(b), Call(a), And } rather than { And,
242  // Call(a), Call(b) }. This way, joining two expressions like "a" 'and' "b"
243  // can be done by appending to a vector, avoiding having to shift all the
244  // elements down to insert the new operation at the head. See the
245  // implementation of Walk() for guidance.
246  std::vector<Op> _ops;
247 
248  // On the contrary, the elements in _calls are in forward-order, so the last
249  // Call in _ops corresponds to the first element of _calls.
250  std::vector<FnCall> _calls;
251 
252  // This member holds a parsing error string if this expression was
253  // constructed by the parser and errors were encountered during the parsing.
254  std::string _parseError;
255 };
256 
258 
259 #endif // PXR_USD_SDF_PREDICATE_EXPRESSION_H
friend bool operator!=(FnArg const &l, FnArg const &r)
static FnArg Keyword(std::string const &name, VtValue const &val)
paren/comma & pos/kw args like 'foo(23, bar=baz)'
GLint left
Definition: glcorearb.h:2005
SDF_API void Walk(TfFunctionRef< void(Op, int)> logic, TfFunctionRef< void(FnCall const &)> call) const
SdfPredicateExpression & operator=(SdfPredicateExpression const &)=default
Copy assign from another expression.
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
GLdouble right
Definition: glad.h:2817
static SDF_API SdfPredicateExpression MakeCall(FnCall &&call)
Produce a new expression containing just a the function call call.
SdfPredicateExpression()=default
Construct the empty expression whose bool-operator returns false.
SDF_API std::string GetText() const
friend bool operator==(FnArg const &l, FnArg const &r)
GLuint const GLchar * name
Definition: glcorearb.h:786
static SDF_API SdfPredicateExpression MakeOp(Op op, SdfPredicateExpression &&left, SdfPredicateExpression &&right)
std::string GetParseError() const &&
#define SDF_API
Definition: api.h:40
SDF_API void WalkWithOpStack(TfFunctionRef< void(std::vector< std::pair< Op, int >> const &)> logic, TfFunctionRef< void(FnCall const &)> call) const
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
static SDF_API SdfPredicateExpression MakeNot(SdfPredicateExpression &&right)
Produce a new expression by prepending the 'not' operator onto right.
colon-separated pos args, like 'isa:Imageable'
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
GLboolean r
Definition: glcorearb.h:1222
#define const
Definition: zconf.h:214
static FnArg Positional(VtValue const &val)
Op
Enumerant describing a subexpression operation.
std::string const & GetParseError() const &
Definition: value.h:167