@@ -2319,10 +2319,10 @@ private Expr step(final boolean error) throws QueryException {
23192319 /**
23202320 * Parses the "AxisStep" rule.
23212321 * @param error show error if nothing is found
2322- * @return step or {@code null}
2322+ * @return step, lookup expression, or {@code null}
23232323 * @throws QueryException query exception
23242324 */
2325- private Step axisStep (final boolean error ) throws QueryException {
2325+ private Expr axisStep (final boolean error ) throws QueryException {
23262326 Axis axis = null ;
23272327 ExprInfo test = null ;
23282328 if (wsConsume (".." )) {
@@ -2372,11 +2372,16 @@ private Step axisStep(final boolean error) throws QueryException {
23722372 checkPred (false );
23732373 }
23742374
2375- // step with node test
2375+ // step with node test or selector
23762376 final InputInfo ii = info ();
2377- if (test instanceof final Test t ) return new CachedStep (ii , axis , t , preds .finish ());
2378- // step with selector
2379- return new SelectorStep (ii , axis , (Expr ) test , preds .finish ());
2377+ final Step step = test instanceof final Test t
2378+ ? new CachedStep (ii , axis , t , preds .finish ())
2379+ : new SelectorStep (ii , axis , (Expr ) test , preds .finish ());
2380+
2381+ // an axis step may be followed by lookups and further predicates (#2591)
2382+ if (!current ('?' )) return step ;
2383+ final Expr lookup = lookup (Path .get (ii , null , step ));
2384+ return lookup == null ? step : postfixOps (lookup , true );
23802385 }
23812386
23822387 /**
@@ -2484,29 +2489,40 @@ private Test simpleNodeTest(final Kind kind, final boolean all) throws QueryExce
24842489 * @throws QueryException query exception
24852490 */
24862491 private Expr postfix () throws QueryException {
2487- Expr expr = primary ();
2488- if (expr != null ) {
2489- while (true ) {
2490- if (wsConsume ("[" )) {
2491- final ExprList el = new ExprList ();
2492- do {
2493- add (el , expr ());
2494- wsCheck ("]" );
2495- } while (wsConsume ("[" ));
2496- expr = new CachedFilter (info (), expr , el .finish ());
2497- } else if (consume ("=?>" )) {
2498- expr = methodCall (expr );
2499- } else if (current ('(' )) {
2500- expr = Functions .dynamic (expr , argumentList (false , null ));
2501- } else if (current ('?' )) {
2502- expr = lookup (expr );
2503- if (expr == null ) break ;
2504- } else {
2505- break ;
2506- }
2492+ final Expr expr = primary ();
2493+ return expr != null ? postfixOps (expr , false ) : null ;
2494+ }
2495+
2496+ /**
2497+ * Parses the postfix operators that may follow a primary expression or an axis step.
2498+ * @param expr input expression
2499+ * @param axis parse the tail of an axis step (only predicates and lookups are allowed)
2500+ * @return resulting expression
2501+ * @throws QueryException query exception
2502+ */
2503+ private Expr postfixOps (final Expr expr , final boolean axis ) throws QueryException {
2504+ Expr result = expr ;
2505+ while (true ) {
2506+ if (wsConsume ("[" )) {
2507+ final ExprList el = new ExprList ();
2508+ do {
2509+ add (el , expr ());
2510+ wsCheck ("]" );
2511+ } while (wsConsume ("[" ));
2512+ result = new CachedFilter (info (), result , el .finish ());
2513+ } else if (!axis && consume ("=?>" )) {
2514+ result = methodCall (result );
2515+ } else if (!axis && current ('(' )) {
2516+ result = Functions .dynamic (result , argumentList (false , null ));
2517+ } else if (current ('?' )) {
2518+ final Expr lookup = lookup (result );
2519+ if (lookup == null ) break ;
2520+ result = lookup ;
2521+ } else {
2522+ break ;
25072523 }
25082524 }
2509- return expr ;
2525+ return result ;
25102526 }
25112527
25122528 /**
0 commit comments