@@ -255,14 +255,15 @@ struct DataTreeQuery::XPathParser
255255 {
256256 // Property selection
257257 ++currentToken;
258- if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::Identifier)
258+ if (currentToken < static_cast <int > (tokens.size ()) &&
259+ (tokens[currentToken].type == Token::Type::Identifier || tokens[currentToken].type == Token::Type::Function))
259260 {
260261 operations.emplace_back (QueryOperation::Property, tokens[currentToken].value );
261262 ++currentToken;
262263 }
263264 else
264265 {
265- // Error: @ not followed by identifier
266+ // Error: @ not followed by identifier or function name
266267 parseResult = Result::fail (" Expected property name after '@' in node test" );
267268 return ;
268269 }
@@ -419,105 +420,100 @@ struct DataTreeQuery::XPathParser
419420 else if (token.type == Token::Type::Function)
420421 {
421422 ++currentToken;
423+ std::unique_ptr<Predicate> pred;
424+
425+ auto skipParenthesis = [&]
426+ {
427+ if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::OpenParen)
428+ {
429+ ++currentToken;
430+ if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::CloseParen)
431+ ++currentToken;
432+ }
433+ };
422434
423435 if (token.value == " first" )
424436 {
437+ skipParenthesis ();
425438 return std::make_unique<Predicate> (Predicate::First);
426439 }
427440 else if (token.value == " last" )
428441 {
442+ skipParenthesis ();
429443 return std::make_unique<Predicate> (Predicate::Last);
430444 }
431445 else if (token.value == " position" )
432446 {
433- // Skip parentheses for position()
434- if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::OpenParen)
435- {
436- ++currentToken;
437- if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::CloseParen)
438- ++currentToken;
439- }
440-
441- // For now, treat position() as position(1) - could be enhanced
447+ skipParenthesis ();
442448 return std::make_unique<Predicate> (Predicate::Position, 1 );
443449 }
444450 }
445451 else if (token.type == Token::Type::AtSign)
446452 {
447453 ++currentToken;
448- if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::Identifier)
454+ if (currentToken < static_cast <int > (tokens.size ()) &&
455+ (tokens[currentToken].type == Token::Type::Identifier || tokens[currentToken].type == Token::Type::Function))
449456 {
450457 String propertyName = tokens[currentToken].value ;
451458 ++currentToken;
452459
460+ auto parseAndValidateValue = [&]
461+ {
462+ ++currentToken;
463+ auto value = parseValue ();
464+ if (! isValidValue (value))
465+ {
466+ parseResult = Result::fail (" Expected value after comparison operator" );
467+ return var ();
468+ }
469+
470+ return value;
471+ };
472+
453473 // Check for equality/inequality
454474 if (currentToken < static_cast <int > (tokens.size ()))
455475 {
456476 if (tokens[currentToken].type == Token::Type::Equal)
457477 {
458- ++currentToken;
459- auto value = parseValue ();
460- if (! isValidValue (value))
461- {
462- parseResult = Result::fail (" Expected value after comparison operator" );
463- return nullptr ;
464- }
465- return std::make_unique<Predicate> (Predicate::PropertyEquals, propertyName, value);
478+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
479+ return std::make_unique<Predicate> (Predicate::PropertyEquals, propertyName, value);
480+
481+ return nullptr ;
466482 }
467483 else if (tokens[currentToken].type == Token::Type::NotEqual)
468484 {
469- ++currentToken;
470- auto value = parseValue ();
471- if (! isValidValue (value))
472- {
473- parseResult = Result::fail (" Expected value after comparison operator" );
474- return nullptr ;
475- }
476- return std::make_unique<Predicate> (Predicate::PropertyNotEquals, propertyName, value);
485+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
486+ return std::make_unique<Predicate> (Predicate::PropertyNotEquals, propertyName, value);
487+
488+ return nullptr ;
477489 }
478490 else if (tokens[currentToken].type == Token::Type::Greater)
479491 {
480- ++currentToken;
481- auto value = parseValue ();
482- if (! isValidValue (value))
483- {
484- parseResult = Result::fail (" Expected value after comparison operator" );
485- return nullptr ;
486- }
487- return std::make_unique<Predicate> (Predicate::PropertyGreater, propertyName, value);
492+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
493+ return std::make_unique<Predicate> (Predicate::PropertyGreater, propertyName, value);
494+
495+ return nullptr ;
488496 }
489497 else if (tokens[currentToken].type == Token::Type::Less)
490498 {
491- ++currentToken;
492- auto value = parseValue ();
493- if (! isValidValue (value))
494- {
495- parseResult = Result::fail (" Expected value after comparison operator" );
496- return nullptr ;
497- }
498- return std::make_unique<Predicate> (Predicate::PropertyLess, propertyName, value);
499+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
500+ return std::make_unique<Predicate> (Predicate::PropertyLess, propertyName, value);
501+
502+ return nullptr ;
499503 }
500504 else if (tokens[currentToken].type == Token::Type::GreaterEqual)
501505 {
502- ++currentToken;
503- auto value = parseValue ();
504- if (! isValidValue (value))
505- {
506- parseResult = Result::fail (" Expected value after comparison operator" );
507- return nullptr ;
508- }
509- return std::make_unique<Predicate> (Predicate::PropertyGreaterEqual, propertyName, value);
506+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
507+ return std::make_unique<Predicate> (Predicate::PropertyGreaterEqual, propertyName, value);
508+
509+ return nullptr ;
510510 }
511511 else if (tokens[currentToken].type == Token::Type::LessEqual)
512512 {
513- ++currentToken;
514- auto value = parseValue ();
515- if (! isValidValue (value))
516- {
517- parseResult = Result::fail (" Expected value after comparison operator" );
518- return nullptr ;
519- }
520- return std::make_unique<Predicate> (Predicate::PropertyLessEqual, propertyName, value);
513+ if (auto value = parseAndValidateValue (); ! value.isVoid ())
514+ return std::make_unique<Predicate> (Predicate::PropertyLessEqual, propertyName, value);
515+
516+ return nullptr ;
521517 }
522518 }
523519
@@ -526,7 +522,7 @@ struct DataTreeQuery::XPathParser
526522 }
527523 else
528524 {
529- // Error: @ not followed by identifier in predicate
525+ // Error: @ not followed by identifier or function name in predicate
530526 parseResult = Result::fail (" Expected property name after '@' in predicate" );
531527 return nullptr ;
532528 }
@@ -704,40 +700,61 @@ struct DataTreeQuery::XPathParser
704700 break ;
705701
706702 case ' !' :
707- if (pos + 1 < input.length () && input[pos + 1 ] == ' =' )
708- {
709- tokens.emplace_back (Token::Type::NotEqual, tokenStart);
710- pos += 2 ;
711- }
712- else
713703 {
714- ++pos; // Skip invalid character
704+ // Check for != with optional whitespace
705+ int nextPos = pos + 1 ;
706+ while (nextPos < input.length () && std::isspace (input[nextPos]))
707+ ++nextPos;
708+
709+ if (nextPos < input.length () && input[nextPos] == ' =' )
710+ {
711+ tokens.emplace_back (Token::Type::NotEqual, tokenStart);
712+ pos = nextPos + 1 ; // Move past the '='
713+ }
714+ else
715+ {
716+ ++pos; // Skip invalid character
717+ }
715718 }
716719 break ;
717720
718721 case ' >' :
719- if (pos + 1 < input.length () && input[pos + 1 ] == ' =' )
720722 {
721- tokens.emplace_back (Token::Type::GreaterEqual, tokenStart);
722- pos += 2 ;
723- }
724- else
725- {
726- tokens.emplace_back (Token::Type::Greater, tokenStart);
727- ++pos;
723+ // Check for >= with optional whitespace
724+ int nextPos = pos + 1 ;
725+ while (nextPos < input.length () && std::isspace (input[nextPos]))
726+ ++nextPos;
727+
728+ if (nextPos < input.length () && input[nextPos] == ' =' )
729+ {
730+ tokens.emplace_back (Token::Type::GreaterEqual, tokenStart);
731+ pos = nextPos + 1 ; // Move past the '='
732+ }
733+ else
734+ {
735+ tokens.emplace_back (Token::Type::Greater, tokenStart);
736+ ++pos; // Just move past '>'
737+ }
728738 }
729739 break ;
730740
731741 case ' <' :
732- if (pos + 1 < input.length () && input[pos + 1 ] == ' =' )
733742 {
734- tokens.emplace_back (Token::Type::LessEqual, tokenStart);
735- pos += 2 ;
736- }
737- else
738- {
739- tokens.emplace_back (Token::Type::Less, tokenStart);
740- ++pos;
743+ // Check for <= with optional whitespace
744+ int nextPos = pos + 1 ;
745+ while (nextPos < input.length () && std::isspace (input[nextPos]))
746+ ++nextPos;
747+
748+ if (nextPos < input.length () && input[nextPos] == ' =' )
749+ {
750+ tokens.emplace_back (Token::Type::LessEqual, tokenStart);
751+ pos = nextPos + 1 ; // Move past the '='
752+ }
753+ else
754+ {
755+ tokens.emplace_back (Token::Type::Less, tokenStart);
756+ ++pos; // Just move past '<'
757+ }
741758 }
742759 break ;
743760
0 commit comments