@@ -39,6 +39,10 @@ struct DataTreeQuery::XPathParser
3939 AtSign, // @
4040 Equal, // =
4141 NotEqual, // !=
42+ Greater, // >
43+ Less, // <
44+ GreaterEqual, // >=
45+ LessEqual, // <=
4246 String, // 'value' or "value"
4347 Number, // 123, 45.67
4448 And, // and
@@ -83,6 +87,10 @@ struct DataTreeQuery::XPathParser
8387 HasProperty, // [@prop]
8488 PropertyEquals, // [@prop='value']
8589 PropertyNotEquals, // [@prop!='value']
90+ PropertyGreater, // [@prop > value]
91+ PropertyLess, // [@prop < value]
92+ PropertyGreaterEqual, // [@prop >= value]
93+ PropertyLessEqual, // [@prop <= value]
8694 Position, // [1], [2], etc.
8795 First, // [first()]
8896 Last, // [last()]
@@ -246,6 +254,22 @@ struct DataTreeQuery::XPathParser
246254
247255 return ; // Property selection terminates node traversal
248256 }
257+ else if (token.type == Token::Type::Function && token.value == " text" )
258+ {
259+ // text() function - select text property
260+ ++currentToken;
261+
262+ // Skip parentheses for text()
263+ if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::OpenParen)
264+ {
265+ ++currentToken;
266+ if (currentToken < static_cast <int > (tokens.size ()) && tokens[currentToken].type == Token::Type::CloseParen)
267+ ++currentToken;
268+ }
269+
270+ operations.emplace_back (QueryOperation::Property, " text" );
271+ return ; // text() selection terminates node traversal
272+ }
249273 else if (token.type == Token::Type::OpenBracket)
250274 {
251275 // Unexpected bracket without node test
@@ -424,6 +448,50 @@ struct DataTreeQuery::XPathParser
424448 }
425449 return std::make_unique<Predicate> (Predicate::PropertyNotEquals, propertyName, value);
426450 }
451+ else if (tokens[currentToken].type == Token::Type::Greater)
452+ {
453+ ++currentToken;
454+ auto value = parseValue ();
455+ if (!isValidValue (value))
456+ {
457+ parseResult = Result::fail (" Expected value after comparison operator" );
458+ return nullptr ;
459+ }
460+ return std::make_unique<Predicate> (Predicate::PropertyGreater, propertyName, value);
461+ }
462+ else if (tokens[currentToken].type == Token::Type::Less)
463+ {
464+ ++currentToken;
465+ auto value = parseValue ();
466+ if (!isValidValue (value))
467+ {
468+ parseResult = Result::fail (" Expected value after comparison operator" );
469+ return nullptr ;
470+ }
471+ return std::make_unique<Predicate> (Predicate::PropertyLess, propertyName, value);
472+ }
473+ else if (tokens[currentToken].type == Token::Type::GreaterEqual)
474+ {
475+ ++currentToken;
476+ auto value = parseValue ();
477+ if (!isValidValue (value))
478+ {
479+ parseResult = Result::fail (" Expected value after comparison operator" );
480+ return nullptr ;
481+ }
482+ return std::make_unique<Predicate> (Predicate::PropertyGreaterEqual, propertyName, value);
483+ }
484+ else if (tokens[currentToken].type == Token::Type::LessEqual)
485+ {
486+ ++currentToken;
487+ auto value = parseValue ();
488+ if (!isValidValue (value))
489+ {
490+ parseResult = Result::fail (" Expected value after comparison operator" );
491+ return nullptr ;
492+ }
493+ return std::make_unique<Predicate> (Predicate::PropertyLessEqual, propertyName, value);
494+ }
427495 }
428496
429497 // Just checking for property existence
@@ -489,13 +557,15 @@ struct DataTreeQuery::XPathParser
489557 QueryOperation op (QueryOperation::Where);
490558
491559 auto predicatePtr = std::shared_ptr<Predicate> (std::move (predicate));
492- op. predicate = [predicatePtr] ( const DataTree& node) -> bool
493- {
494- return evaluatePredicate (* predicatePtr, node, 0 , 1 ); // position and size will be set during execution
495- };
560+
561+ // Store the predicate for position-aware evaluation
562+ op. xpathPredicate = predicatePtr;
563+
496564 operations.push_back (std::move (op));
497565 }
498566
567+
568+ public:
499569 static bool evaluatePredicate (const Predicate& predicate, const DataTree& node, int position, int totalCount)
500570 {
501571 switch (predicate.type )
@@ -509,6 +579,26 @@ struct DataTreeQuery::XPathParser
509579 case Predicate::PropertyNotEquals:
510580 return ! node.hasProperty (predicate.property ) || node.getProperty (predicate.property ) != predicate.value ;
511581
582+ case Predicate::PropertyGreater:
583+ if (!node.hasProperty (predicate.property ))
584+ return false ;
585+ return node.getProperty (predicate.property ) > predicate.value ;
586+
587+ case Predicate::PropertyLess:
588+ if (!node.hasProperty (predicate.property ))
589+ return false ;
590+ return node.getProperty (predicate.property ) < predicate.value ;
591+
592+ case Predicate::PropertyGreaterEqual:
593+ if (!node.hasProperty (predicate.property ))
594+ return false ;
595+ return node.getProperty (predicate.property ) >= predicate.value ;
596+
597+ case Predicate::PropertyLessEqual:
598+ if (!node.hasProperty (predicate.property ))
599+ return false ;
600+ return node.getProperty (predicate.property ) <= predicate.value ;
601+
512602 case Predicate::Position:
513603 return position == predicate.position - 1 ; // XPath is 1-indexed
514604
@@ -531,6 +621,7 @@ struct DataTreeQuery::XPathParser
531621 return false ;
532622 }
533623
624+ private:
534625 void tokenize ()
535626 {
536627 pos = 0 ;
@@ -598,6 +689,32 @@ struct DataTreeQuery::XPathParser
598689 }
599690 break ;
600691
692+ case ' >' :
693+ if (pos + 1 < input.length () && input[pos + 1 ] == ' =' )
694+ {
695+ tokens.emplace_back (Token::Type::GreaterEqual, tokenStart);
696+ pos += 2 ;
697+ }
698+ else
699+ {
700+ tokens.emplace_back (Token::Type::Greater, tokenStart);
701+ ++pos;
702+ }
703+ break ;
704+
705+ case ' <' :
706+ if (pos + 1 < input.length () && input[pos + 1 ] == ' =' )
707+ {
708+ tokens.emplace_back (Token::Type::LessEqual, tokenStart);
709+ pos += 2 ;
710+ }
711+ else
712+ {
713+ tokens.emplace_back (Token::Type::Less, tokenStart);
714+ ++pos;
715+ }
716+ break ;
717+
601718 case ' (' :
602719 tokens.emplace_back (Token::Type::OpenParen, tokenStart);
603720 ++pos;
@@ -683,7 +800,7 @@ struct DataTreeQuery::XPathParser
683800 else if (identifier == " not" )
684801 tokens.emplace_back (Token::Type::Not, start);
685802
686- else if (identifier == " first" || identifier == " last" || identifier == " position" || identifier == " count" )
803+ else if (identifier == " first" || identifier == " last" || identifier == " position" || identifier == " count" || identifier == " text " )
687804 tokens.emplace_back (Token::Type::Function, identifier, start);
688805
689806 else
@@ -1135,8 +1252,21 @@ std::vector<DataTree> DataTreeQuery::applyOperation (const QueryOperation& op, c
11351252
11361253 case QueryOperation::Where:
11371254 {
1138- if (op.predicate )
1255+ if (op.xpathPredicate )
1256+ {
1257+ // XPath predicate with position information
1258+ auto predicate = std::static_pointer_cast<XPathParser::Predicate>(op.xpathPredicate );
1259+ int totalCount = static_cast <int >(input.size ());
1260+ for (int i = 0 ; i < static_cast <int >(input.size ()); ++i)
1261+ {
1262+ const auto & node = input[i];
1263+ if (XPathParser::evaluatePredicate (*predicate, node, i, totalCount))
1264+ result.push_back (node);
1265+ }
1266+ }
1267+ else if (op.predicate )
11391268 {
1269+ // Regular predicate (from fluent API)
11401270 for (const auto & node : input)
11411271 {
11421272 if (op.predicate (node))
0 commit comments