@@ -2482,10 +2482,10 @@ private Expr step(final boolean error) throws QueryException {
24822482 /**
24832483 * Parses the "AxisStep" rule.
24842484 * @param error show error if nothing is found
2485- * @return step or {@code null}
2485+ * @return step, lookup expression, or {@code null}
24862486 * @throws QueryException query exception
24872487 */
2488- private Step axisStep (final boolean error ) throws QueryException {
2488+ private Expr axisStep (final boolean error ) throws QueryException {
24892489 Axis axis = null ;
24902490 ExprInfo test = null ;
24912491 if (wsConsume (".." )) {
@@ -2535,11 +2535,16 @@ private Step axisStep(final boolean error) throws QueryException {
25352535 checkPred (false );
25362536 }
25372537
2538- // step with node test
2538+ // step with node test or selector
25392539 final InputInfo ii = info ();
2540- if (test instanceof final Test t ) return new CachedStep (ii , axis , t , preds .finish ());
2541- // step with selector
2542- return new SelectorStep (ii , axis , (Expr ) test , preds .finish ());
2540+ final Step step = test instanceof final Test t
2541+ ? new CachedStep (ii , axis , t , preds .finish ())
2542+ : new SelectorStep (ii , axis , (Expr ) test , preds .finish ());
2543+
2544+ // an axis step may be followed by lookups and further predicates (#2591)
2545+ if (!current ('?' )) return step ;
2546+ final Expr lookup = lookup (Path .get (ii , null , step ));
2547+ return lookup == null ? step : postfixOps (lookup , true );
25432548 }
25442549
25452550 /**
@@ -2647,29 +2652,40 @@ private Test simpleNodeTest(final Kind kind, final boolean all) throws QueryExce
26472652 * @throws QueryException query exception
26482653 */
26492654 private Expr postfix () throws QueryException {
2650- Expr expr = primary ();
2651- if (expr != null ) {
2652- while (true ) {
2653- if (wsConsume ("[" )) {
2654- final ExprList el = new ExprList ();
2655- do {
2656- add (el , expr ());
2657- wsCheck ("]" );
2658- } while (wsConsume ("[" ));
2659- expr = new CachedFilter (info (), expr , el .finish ());
2660- } else if (consume ("=?>" )) {
2661- expr = methodCall (expr );
2662- } else if (current ('(' )) {
2663- expr = Functions .dynamic (expr , argumentList (false , null ));
2664- } else if (current ('?' )) {
2665- expr = lookup (expr );
2666- if (expr == null ) break ;
2667- } else {
2668- break ;
2669- }
2655+ final Expr expr = primary ();
2656+ return expr != null ? postfixOps (expr , false ) : null ;
2657+ }
2658+
2659+ /**
2660+ * Parses the postfix operators that may follow a primary expression or an axis step.
2661+ * @param expr input expression
2662+ * @param axis parse the tail of an axis step (only predicates and lookups are allowed)
2663+ * @return resulting expression
2664+ * @throws QueryException query exception
2665+ */
2666+ private Expr postfixOps (final Expr expr , final boolean axis ) throws QueryException {
2667+ Expr result = expr ;
2668+ while (true ) {
2669+ if (wsConsume ("[" )) {
2670+ final ExprList el = new ExprList ();
2671+ do {
2672+ add (el , expr ());
2673+ wsCheck ("]" );
2674+ } while (wsConsume ("[" ));
2675+ result = new CachedFilter (info (), result , el .finish ());
2676+ } else if (!axis && consume ("=?>" )) {
2677+ result = methodCall (result );
2678+ } else if (!axis && current ('(' )) {
2679+ result = Functions .dynamic (result , argumentList (false , null ));
2680+ } else if (current ('?' )) {
2681+ final Expr lookup = lookup (result );
2682+ if (lookup == null ) break ;
2683+ result = lookup ;
2684+ } else {
2685+ break ;
26702686 }
26712687 }
2672- return expr ;
2688+ return result ;
26732689 }
26742690
26752691 /**
0 commit comments