@@ -41,6 +41,7 @@ enum QueryBuilderState {
4141 IN_END_QUERY,
4242 IN_STRING,
4343 IN_NUMBER,
44+ IN_NEGATION,
4445 IN_ERROR,
4546 IN_BEGIN_ATTRIBUTES,
4647 IN_END_ATTRIBUTES,
@@ -82,6 +83,22 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
8283 states.push_back (state);
8384 };
8485
86+ auto checkModifier = [&states, &cur, &next, &error](QueryBuilderState state, const char * mod) {
87+ const char * modifier = nullptr ;
88+ if (mod[0 ] != ' \0 ' && (modifier = strpbrk (cur, mod)) != nullptr ) {
89+ switch (*modifier) {
90+ case ' !' :
91+ states.push_back (IN_NEGATION);
92+ break ;
93+ default :
94+ error (" invalid modifier '" + std::string (modifier, 1 ) + " '" );
95+ return ;
96+ }
97+ next = ++cur;
98+ }
99+ states.push_back (state);
100+ };
101+
85102 auto token = [&states, &expectedSeparators](QueryBuilderState state, char const * sep) {
86103 states.push_back (state);
87104 expectedSeparators = sep;
@@ -143,6 +160,18 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
143160 return InputSpec{binding, std::move (*lastMatcher.release ()), lifetime, attributes};
144161 };
145162
163+ auto pushMatcher = [&nodes, &states](auto && matcher) {
164+ if (states.back () == IN_NEGATION) {
165+ states.pop_back ();
166+ auto notMatcher = std::make_unique<DataDescriptorMatcher>(DataDescriptorMatcher::Op::Xor,
167+ std::move (matcher),
168+ data_matcher::ConstantValueMatcher{true });
169+ nodes.push_back (std::move (notMatcher));
170+ } else {
171+ nodes.push_back (std::move (matcher));
172+ }
173+ };
174+
146175 while (states.empty () == false ) {
147176 auto const state = states.back ();
148177 states.pop_back ();
@@ -224,21 +253,22 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
224253 }
225254 } break ;
226255 case IN_BEGIN_SUBSPEC: {
227- pushState (IN_END_SUBSPEC);
256+ checkModifier (IN_END_SUBSPEC, " ! " );
228257 token (IN_NUMBER, " ;%?" );
229258 } break ;
230259 case IN_END_SUBSPEC: {
231260 if (*next == ' %' && assignLastNumericMatch (" subspec" , currentSubSpec, IN_BEGIN_TIMEMODULO)) {
232- nodes.push_back (SubSpecificationTypeValueMatcher{*currentSubSpec});
233261 } else if (*next == ' ?' && assignLastNumericMatch (" subspec" , currentSubSpec, IN_BEGIN_ATTRIBUTES)) {
234- nodes.push_back (SubSpecificationTypeValueMatcher{*currentSubSpec});
235262 } else if (*next == ' ;' && assignLastNumericMatch (" subspec" , currentSubSpec, IN_END_MATCHER)) {
236- nodes.push_back (SubSpecificationTypeValueMatcher{*currentSubSpec});
237263 } else if (*next == ' \0 ' && assignLastNumericMatch (" subspec" , currentSubSpec, IN_END_MATCHER)) {
238- nodes.push_back (SubSpecificationTypeValueMatcher{*currentSubSpec});
239264 } else {
240265 error (" Expected a number" );
266+ break ;
241267 }
268+ auto backup = states.back ();
269+ states.pop_back ();
270+ pushMatcher (SubSpecificationTypeValueMatcher{*currentSubSpec});
271+ states.push_back (backup);
242272 } break ;
243273 case IN_BEGIN_TIMEMODULO: {
244274 pushState (IN_END_TIMEMODULO);
@@ -306,6 +336,9 @@ std::vector<InputSpec> DataDescriptorQueryBuilder::parse(char const* config)
306336 case IN_END_ATTRIBUTES: {
307337 pushState (IN_END_MATCHER);
308338 } break ;
339+ case IN_NEGATION: {
340+ error (" property modifiers should have been handled before when inserting previous matcher" );
341+ } break ;
309342 case IN_ERROR: {
310343 throw std::runtime_error (" Parse error: " + errorString);
311344 } break ;
0 commit comments