Loading...
Searching...
No Matches
predicateExpressionParser.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
8#ifndef PXR_USD_SDF_PREDICATE_EXPRESSION_PARSER_H
9#define PXR_USD_SDF_PREDICATE_EXPRESSION_PARSER_H
10
11#include "pxr/pxr.h"
12#include "pxr/usd/sdf/api.h"
13
15#include "pxr/base/vt/value.h"
16
17#include "pxr/base/pegtl/pegtl.hpp"
18
19#include <memory>
20
21PXR_NAMESPACE_OPEN_SCOPE
22
23// fwd decl, from parserHelpers.cpp.
24std::string
25Sdf_EvalQuotedString(const char* x, size_t n,
26 size_t trimBothSides, unsigned int* numLines=NULL);
27
28struct SdfPredicateExprBuilder
29{
30 SdfPredicateExprBuilder() { OpenGroup(); }
31
32 void PushOp(SdfPredicateExpression::Op op) { _stacks.back().PushOp(op); }
33
34 void PushCall(SdfPredicateExpression::FnCall::Kind kind) {
35 _stacks.back().PushCall(
36 kind, std::move(_funcName), std::move(_funcArgs));
37 _funcName.clear();
38 _funcArgs.clear();
39 }
40
41 void SetFuncName(std::string const &name) {
42 _funcName = name;
43 }
44
45 void AddFuncArg(VtValue const &val) {
46 _funcArgs.push_back({ std::move(_funcKwArgName), val });
47 _funcKwArgName.clear();
48 }
49
50 void SetFuncArgKWName(std::string const &kw) {
51 _funcKwArgName = kw;
52 }
53
54 void OpenGroup() { _stacks.emplace_back(); }
55
56 void CloseGroup() {
57 SdfPredicateExpression innerExpr = _stacks.back().Finish();
58 _stacks.pop_back();
59 _stacks.back().PushExpr(std::move(innerExpr));
60 }
61
62 SdfPredicateExpression Finish() {
63 SdfPredicateExpression result = _stacks.back().Finish();
64 _stacks.clear();
65 _funcArgs.clear();
66 _funcName.clear();
67 return result;
68 }
69
70private:
71 struct _Stack {
72
73 void PushOp(SdfPredicateExpression::Op op) {
75 auto higherPrec = [](Op left, Op right) {
76 return (left < right) || (left == right && left != Op::Not);
77 };
78 // Reduce while prior ops have higher precedence.
79 while (!opStack.empty() && higherPrec(opStack.back(), op)) {
80 _Reduce();
81 }
82 opStack.push_back(op);
83 }
84
85 void PushCall(SdfPredicateExpression::FnCall::Kind kind,
86 std::string &&name,
87 std::vector<SdfPredicateExpression::FnArg> &&args) {
88 exprStack.push_back(
90 kind, std::move(name), std::move(args) }));
91 }
92
93 void PushExpr(SdfPredicateExpression &&expr) {
94 exprStack.push_back(std::move(expr));
95 }
96
97 SdfPredicateExpression Finish() {
98 while (!opStack.empty()) {
99 _Reduce();
100 }
101 SdfPredicateExpression ret = std::move(exprStack.back());
102 exprStack.clear();
103 return ret;
104 }
105
106 private:
107 void _Reduce() {
108 SdfPredicateExpression::Op op = opStack.back();
109 opStack.pop_back();
110 SdfPredicateExpression right = std::move(exprStack.back());
111 exprStack.pop_back();
112
113 if (op == SdfPredicateExpression::Not) {
114 // Not is the only unary op.
115 exprStack.push_back(
116 SdfPredicateExpression::MakeNot(std::move(right)));
117 }
118 else {
119 // All other ops are binary.
120 SdfPredicateExpression left = std::move(exprStack.back());
121 exprStack.pop_back();
122 exprStack.push_back(
124 op, std::move(left), std::move(right))
125 );
126 }
127 }
128
129 // Working space.
130 std::vector<SdfPredicateExpression::Op> opStack;
131 std::vector<SdfPredicateExpression> exprStack;
132 };
133
134 std::vector<_Stack> _stacks;
135
136 std::string _funcName;
137 std::string _funcKwArgName;
138 std::vector<SdfPredicateExpression::FnArg> _funcArgs;
139};
140
141
142
144// Grammar.
145
146namespace SdfPredicateExpressionParser {
147
148using namespace PXR_PEGTL_NAMESPACE;
149
150template <class Rule> using OptSpaced = pad<Rule, blank>;
151
152using OptSpacedComma = OptSpaced<one<','>>;
153
155// Predicate expression grammar.
156
157struct NotKW : PXR_PEGTL_KEYWORD("not") {};
158struct AndKW : PXR_PEGTL_KEYWORD("and") {};
159struct OrKW : PXR_PEGTL_KEYWORD("or") {};
160struct Inf : PXR_PEGTL_KEYWORD("inf") {};
161struct True : PXR_PEGTL_KEYWORD("true") {};
162struct False : PXR_PEGTL_KEYWORD("false") {};
163struct ImpliedAnd : plus<blank> {};
164
165struct ReservedWord : sor<
166 NotKW, AndKW, OrKW, Inf, True, False> {};
167
168struct Digits : plus<range<'0','9'>> {};
169
170struct Exp : seq<one<'e','E'>, opt<one<'-','+'>>, must<Digits>> {};
171struct Frac : if_must<one<'.'>, Digits> {};
172struct PredArgFloat : seq<
173 opt<one<'-'>>, sor<Inf, seq<Digits, if_then_else<Frac, opt<Exp>, Exp>>>
174 > {};
175struct PredArgInt : seq<opt<one<'-'>>, Digits> {};
176
177struct PredArgBool : sor<True, False> {};
178
179template <class Quote>
180struct Escaped : sor<Quote, one<'\\', 'b', 'f', 'n', 'r', 't'>> {};
181template <class Quote>
182struct Unescaped : minus<utf8::range<0x20, 0x10FFFF>, Quote> {};
183
184template <class Quote>
185struct StringChar : if_then_else<
186 one<'\\'>, must<Escaped<Quote>>, Unescaped<Quote>> {};
187
188struct QuotedString : sor<
189 if_must<one<'"'>, until<one<'"'>, StringChar<one<'"'>>>>,
190 if_must<one<'\''>, until<one<'\''>, StringChar<one<'\''>>>>
191 > {};
192
193struct UnquotedStringChar
194 : sor<identifier_other,
195 one<'~', '!', '@', '#', '$', '%', '^', '&', '*', '-', '+', '=',
196 '|', '\\', '.', '?', '/'>> {};
197
198struct UnquotedString : star<UnquotedStringChar> {};
199
200struct PredArgString : sor<QuotedString, UnquotedString> {};
201
202struct PredArgVal : sor<
203 PredArgFloat, PredArgInt, PredArgBool, PredArgString> {};
204
205struct PredKWArgName : minus<identifier, ReservedWord> {};
206
207struct PredKWArgPrefix : seq<PredKWArgName, OptSpaced<one<'='>>> {};
208struct PredKWArg : if_must<PredKWArgPrefix, PredArgVal> {};
209
210struct PredParenPosArg : seq<not_at<PredKWArgPrefix>, PredArgVal> {};
211
212struct PredFuncName : minus<identifier, ReservedWord> {};
213
214// Within keyword args, ',' unambiguously requires another keyword arg.
215struct PredKWArgStep : if_must<OptSpacedComma, PredKWArg> {};
216struct PredKWArgs : seq<PredKWArg, star<PredKWArgStep>> {};
217
218// The positional-to-keyword transition uses lookahead in the positional list
219// because a ',' after a positional arg could precede either another positional
220// arg or a keyword arg -- we can't commit to PredParenPosArg specifically.
221// Once all positional args are consumed, a ',' commits to requiring kwargs.
222struct PredParenArgs
223 : if_then_else<list<PredParenPosArg, OptSpacedComma>,
224 opt<if_must<OptSpacedComma, PredKWArgs>>,
225 opt<PredKWArgs>>
226{};
227
228struct PredColonArgs;
229struct PredColonArgStep : if_must<one<','>, PredArgVal> {};
230struct PredColonArgs : seq<PredArgVal, star<PredColonArgStep>> {};
231
232struct PredColonCall : if_must<seq<PredFuncName, one<':'>>, PredColonArgs> {};
233
234// Named for error messaging.
235struct PredParenClose : one<')'> {};
236struct PredParenCall : seq<
237 PredFuncName, OptSpaced<one<'('>>,
238 must<PredParenArgs, star<blank>, PredParenClose>
239 >
240{};
241
242struct PredBareCall : PredFuncName {};
243
244struct PredExpr;
245
246struct PredOpenGroup : one<'('> {};
247struct PredCloseGroup : one<')'> {};
248
249// Named wrapper so the group's inner expression gets a targeted error message.
250struct GroupedPredExpr : OptSpaced<PredExpr> {};
251
252struct PredAtom
253 : sor<
254 PredColonCall,
255 PredParenCall,
256 PredBareCall,
257 if_must<PredOpenGroup, GroupedPredExpr, PredCloseGroup>
258 >
259{};
260
261struct PredFactor : seq<opt<OptSpaced<list<NotKW, plus<blank>>>>, PredAtom> {};
262
263// 'and'/'or' are unambiguous operators -- commit to requiring another factor.
264// Implied and (whitespace) needs a lookahead because trailing whitespace is
265// valid and must not commit to requiring another factor.
266// Note: the lookahead parses ImpliedAnd+PredFactor twice; acceptable given
267// the bounded syntax involved.
268struct PredExplicitOp : sor<OptSpaced<AndKW>, OptSpaced<OrKW>> {};
269struct PredExprStep : if_must<PredExplicitOp, PredFactor> {};
270struct ImpliedAndStep : seq<
271 at<seq<ImpliedAnd, PredFactor>>, ImpliedAnd, PredFactor> {};
272
273struct PredExpr : seq<PredFactor, star<sor<PredExprStep, ImpliedAndStep>>> {};
274
275// Wrap eolf so we can attach a targeted error message without specializing
276// the PEGTL built-in directly.
277struct PredExprEnd : eolf {};
278
280// Errors.
281//
282// This is the root of the Errors inheritance chain used across the family
283// of grammars that embed predicate expressions:
284//
285// SdfPredicateExpressionParser::Errors (predicate rules, this file)
286// <- SdfPathPatternParser::Errors (path pattern rules)
287// <- pathExpression.cpp Errors (path expression rules)
288//
289// Each layer in the chain handles only its own grammar's rules; errors from
290// embedded grammars propagate upward automatically via inheritance.
291//
292// inline on static member definitions avoids ODR violations since this
293// header is included in multiple translation units.
294
295template <class Rule>
296struct Errors : public normal<Rule>
297{
298 static char const *message;
299
300 template <class Input, class... States>
301 [[noreturn]] static void raise(Input const &in, States &&...) {
302 throw parse_error(message, in);
303 }
304};
305
306template <class Rule>
307inline char const *Errors<Rule>::message = "";
308
309#define PARSE_ERROR(rule, msg) \
310 template <> inline char const *Errors<rule>::message = msg
311
312PARSE_ERROR(PredExprEnd, "expected end of predicate expression");
313PARSE_ERROR(Digits, "expected digits");
314PARSE_ERROR(Escaped<one<'\''>>, "expected escape character after '\\'");
315PARSE_ERROR(Escaped<one<'"'>>, "expected escape character after '\\'");
316PARSE_ERROR(PredArgVal, "expected argument value after '='");
317PARSE_ERROR(PredColonArgs, "expected argument list after ':'");
318PARSE_ERROR(PredKWArg, "expected keyword argument after ','");
319PARSE_ERROR(PredKWArgs, "expected keyword argument after ','");
320PARSE_ERROR(PredParenClose, "expected ')' to close function call");
321PARSE_ERROR(GroupedPredExpr, "expected predicate expression after '('");
322PARSE_ERROR(PredCloseGroup, "expected ')' to close predicate expression "
323 "group");
324PARSE_ERROR(PredFactor, "expected predicate expression after operator");
325
326#undef PARSE_ERROR
327
328// Actions ///////////////////////////////////////////////////////////////
329
330template <class Rule>
331struct PredAction : nothing<Rule> {};
332
333template <SdfPredicateExpression::Op op>
334struct PredOpAction
335{
336 template <class Input>
337 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
338 builder.PushOp(op);
339 }
340};
341
342template <> struct PredAction<NotKW>
343 : PredOpAction<SdfPredicateExpression::Not> {};
344template <> struct PredAction<AndKW>
345 : PredOpAction<SdfPredicateExpression::And> {};
346template <> struct PredAction<OrKW>
347 : PredOpAction<SdfPredicateExpression::Or> {};
348template <> struct PredAction<ImpliedAnd>
349 : PredOpAction<SdfPredicateExpression::ImpliedAnd> {};
350
351template <>
352struct PredAction<PredOpenGroup>
353{
354 template <class Input>
355 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
356 builder.OpenGroup();
357 }
358};
359
360template <>
361struct PredAction<PredCloseGroup>
362{
363 template <class Input>
364 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
365 builder.CloseGroup();
366 }
367};
368
369template <>
370struct PredAction<PredFuncName>
371{
372 template <class Input>
373 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
374 builder.SetFuncName(in.string());
375 }
376};
377
378template <>
379struct PredAction<PredArgInt>
380{
381 template <class Input>
382 static bool apply(Input const &in, SdfPredicateExprBuilder &builder) {
383 bool outOfRange = false;
384 int64_t ival = TfStringToInt64(in.string(), &outOfRange);
385 if (outOfRange) {
386 return false;
387 }
388 builder.AddFuncArg(VtValue(ival));
389 return true;
390 }
391};
392
393template <>
394struct PredAction<PredArgBool>
395{
396 template <class Input>
397 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
398 builder.AddFuncArg(VtValue(in.string()[0] == 't'));
399 }
400};
401
402template <>
403struct PredAction<PredArgFloat>
404{
405 template <class Input>
406 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
407 std::string const &instr = in.string();
408 double fval;
409 if (instr == "inf") {
410 fval = std::numeric_limits<double>::infinity();
411 }
412 else if (instr == "-inf") {
413 fval = -std::numeric_limits<double>::infinity();
414 }
415 else {
416 fval = TfStringToDouble(instr);
417 }
418 builder.AddFuncArg(VtValue(fval));
419 }
420};
421
422template <>
423struct PredAction<PredArgString>
424{
425 template <class Input>
426 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
427 std::string const &instr = in.string();
428 size_t trimAmount = 0;
429 if (instr.size() >= 2 &&
430 ((instr.front() == '"' && instr.back() == '"') ||
431 (instr.front() == '\'' && instr.back() == '\''))) {
432 trimAmount = 1;
433 }
434 builder.AddFuncArg(
435 VtValue(Sdf_EvalQuotedString(
436 instr.c_str(), instr.size(), trimAmount)));
437 }
438};
439
440template <>
441struct PredAction<PredKWArgName>
442{
443 template <class Input>
444 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
445 builder.SetFuncArgKWName(in.string());
446 }
447};
448
449template <SdfPredicateExpression::FnCall::Kind callKind>
450struct PredCallAction
451{
452 template <class Input>
453 static void apply(Input const &in, SdfPredicateExprBuilder &builder) {
454 builder.PushCall(callKind);
455 }
456};
457template <> struct PredAction<PredBareCall>
458 : PredCallAction<SdfPredicateExpression::FnCall::BareCall> {};
459template <> struct PredAction<PredParenCall>
460 : PredCallAction<SdfPredicateExpression::FnCall::ParenCall> {};
461template <> struct PredAction<PredColonCall>
462 : PredCallAction<SdfPredicateExpression::FnCall::ColonCall> {};
463
464} // SdfPredicateExpressionParser
465
466PXR_NAMESPACE_CLOSE_SCOPE
467
468#endif // PXR_USD_SDF_PREDICATE_EXPRESSION_PARSER_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Represents a logical expression syntax tree consisting of predicate function calls joined by the logi...
Op
Enumerant describing a subexpression operation.
static SDF_API SdfPredicateExpression MakeOp(Op op, SdfPredicateExpression &&left, SdfPredicateExpression &&right)
Produce a new expression by combining left and right with the operator op.
static SDF_API SdfPredicateExpression MakeNot(SdfPredicateExpression &&right)
Produce a new expression by prepending the 'not' operator onto right.
static SDF_API SdfPredicateExpression MakeCall(FnCall &&call)
Produce a new expression containing just a the function call call.
Provides a container which may hold any type, and provides introspection and iteration over array typ...
Definition value.h:90
TF_API int64_t TfStringToInt64(const std::string &txt, bool *outOfRange=NULL)
Convert a sequence of digits in txt to an int64_t value.
TF_API double TfStringToDouble(const std::string &txt)
Converts text string to double.