Skip to content

Commit 610ba97

Browse files
Grammar: make step?lookup valid. qtspecs#1591
1 parent 8f98a75 commit 610ba97

2 files changed

Lines changed: 55 additions & 27 deletions

File tree

basex-core/src/main/java/org/basex/query/QueryParser.java

Lines changed: 43 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -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
/**

basex-core/src/test/java/org/basex/query/expr/LookupTest.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,18 @@ public final class LookupTest extends SandboxTest {
7272
query("array:for-each([], function($i) { string($i) })!?([])", "");
7373
}
7474

75+
/** Lookup directly following an axis step (#2591). */
76+
@Test public void step() {
77+
query("{ 'a': 1, 'b': 2 } ! self::jnode()?b", 2);
78+
query("{ 'a': 1, 'b': 2 } ! self::jnode()?*", "1\n2");
79+
query("{ 'a': { 'b': 5 } } ! self::jnode()?a?b", 5);
80+
// predicate before and after the lookup
81+
query("{ 'a': 1 } ! self::jnode()[1]?a", 1);
82+
query("{ 'a': (1, 2, 3) } ! self::jnode()?a[. > 1]", "2\n3");
83+
// one lookup per context item
84+
query("({ 'k': 7 }, { 'k': 8 }) ! self::jnode()?k", "7\n8");
85+
}
86+
7587
/** Test. */
7688
@Test public void error() {
7789
error("1?a", LOOKUP_X);

0 commit comments

Comments
 (0)