Skip to content

Commit 15c8981

Browse files
committed
[fix] allow function types in context item
- add tests for filtering function types (predicates) - add testcases for lookups and calls after simple map operator - add ContextItemExpression as a special LocationStep The return type is determined by the static return type. If function types are in context item or sequence they will be returned unchanged. As function types cannot be in an index at the moment. - modify XQuery grammar and XQueryTree parser to recognize SELF as an ContextItemExpression, which must be a postFixExpression for predicates and lookups to work
1 parent 9ad24bd commit 15c8981

File tree

4 files changed

+286
-2
lines changed

4 files changed

+286
-2
lines changed

exist-core/src/main/antlr/org/exist/xquery/parser/XQueryTree.g

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2091,6 +2091,14 @@ throws PermissionDeniedException, EXistException, XPathException
20912091
step=postfixExpr [step]
20922092
{ path.add(step); }
20932093
|
2094+
s:SELF
2095+
{
2096+
step= new ContextItemExpression(context);
2097+
step.setASTNode(s);
2098+
}
2099+
step=postfixExpr [step]
2100+
{ path.add(step); }
2101+
|
20942102
#(
20952103
PARENTHESIZED
20962104
{ PathExpr pathExpr= new PathExpr(context); }
@@ -2560,7 +2568,7 @@ throws PermissionDeniedException, EXistException, XPathException
25602568
|
25612569
SELF
25622570
{
2563-
step= new LocationStep(context, Constants.SELF_AXIS, new TypeTest(Type.NODE));
2571+
step = new ContextItemExpression(context);
25642572
path.add(step);
25652573
}
25662574
( predicate [(LocationStep) step] )*
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* eXist-db Open Source Native XML Database
3+
* Copyright (C) 2001 The eXist-db Authors
4+
*
5+
6+
* http://www.exist-db.org
7+
*
8+
* This library is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU Lesser General Public
10+
* License as published by the Free Software Foundation; either
11+
* version 2.1 of the License, or (at your option) any later version.
12+
*
13+
* This library is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
* Lesser General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Lesser General Public
19+
* License along with this library; if not, write to the Free Software
20+
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
*/
22+
package org.exist.xquery;
23+
24+
import org.exist.xquery.util.ExpressionDumper;
25+
import org.exist.xquery.value.Item;
26+
import org.exist.xquery.value.Sequence;
27+
import org.exist.xquery.value.Type;
28+
import static org.exist.xquery.ErrorCodes.XPDY0002;
29+
30+
public class ContextItemExpression extends LocationStep {
31+
32+
private int returnType = Type.ITEM;
33+
34+
public ContextItemExpression(final XQueryContext context) {
35+
// TODO: create class AnyItemTest (one private implementation found in saxon)
36+
super(context, Constants.SELF_AXIS, new AnyNodeTest());
37+
}
38+
39+
@Override
40+
public void analyze(AnalyzeContextInfo contextInfo) throws XPathException {
41+
contextInfo.addFlag(DOT_TEST);
42+
// set return type to static type to allow for index use in optimization step
43+
returnType = contextInfo.getStaticType();
44+
45+
super.analyze(contextInfo);
46+
}
47+
48+
@Override
49+
public Sequence eval(final Sequence contextSequence, final Item contextItem) throws XPathException {
50+
if (contextSequence == null && contextItem == null) {
51+
throw new XPathException(this, XPDY0002, "Nothing to select");
52+
}
53+
54+
// function types in context item are returned unchanged
55+
if (contextItem != null &&
56+
Type.subTypeOf(contextItem.getType(), Type.FUNCTION_REFERENCE)) {
57+
return contextItem.toSequence();
58+
}
59+
60+
// function types in context sequence are returned unchanged
61+
if (contextSequence != null &&
62+
Type.subTypeOf(contextSequence.getItemType(), Type.FUNCTION_REFERENCE)) {
63+
return contextSequence;
64+
}
65+
66+
// handle everything else in super class
67+
return super.eval(contextSequence, contextItem);
68+
}
69+
70+
@Override
71+
public int returnsType() {
72+
return returnType;
73+
}
74+
75+
@Override
76+
public void dump(final ExpressionDumper dumper) {
77+
dumper.display(".");
78+
}
79+
80+
}

exist-core/src/test/xquery/xquery3/bang.xql

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
: License along with this library; if not, write to the Free Software
2020
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
2121
:)
22-
xquery version "3.0";
22+
xquery version "3.1";
2323

2424
module namespace bang="http://exist-db.org/xquery/test/bang";
2525

@@ -202,3 +202,90 @@ declare
202202
function bang:element-left-number-attribute-right() {
203203
<emp id='1'/> ! number(@id)
204204
};
205+
206+
declare
207+
%test:assertEquals(1,2,3)
208+
function bang:map-get () {
209+
(map {'a':1}, map {'a':2}, map {'a':3}) ! map:get(., 'a')
210+
};
211+
212+
declare
213+
%test:assertEquals(1,2,3)
214+
function bang:array-get () {
215+
([1], [2], [3]) ! array:get(., 1)
216+
};
217+
218+
declare
219+
%test:assertEquals(1,2,3)
220+
function bang:map-lookup-context-item () {
221+
(map {'a':1}, map {'a':2}, map {'a':3}) ! .?a
222+
};
223+
224+
declare
225+
%test:assertEquals(1,2,3)
226+
function bang:map-lookup-parenthesized () {
227+
(map {'a':1}, map {'a':2}, map {'a':3}) ! (.?a)
228+
};
229+
230+
declare
231+
%test:assertEquals(1,2,3)
232+
function bang:map-lookup-parenthesized-context-item () {
233+
(map {'a':1}, map {'a':2}, map {'a':3}) ! (.)?a
234+
};
235+
236+
declare
237+
%test:assertEquals(1,2,3)
238+
function bang:array-lookup-standard () {
239+
([1], [2], [3]) ! .?1
240+
};
241+
242+
declare
243+
%test:assertEquals(1,2,3)
244+
function bang:array-lookup-parenthesized () {
245+
([1], [2], [3]) ! (.?1)
246+
};
247+
248+
declare
249+
%test:assertEquals(1,2,3)
250+
function bang:array-lookup-parenthesized-context-item () {
251+
([1], [2], [3]) ! (.)?1
252+
};
253+
254+
declare
255+
%test:assertEquals(1,1,1,1,1)
256+
function bang:mixed-function-types-call () {
257+
let $id := function ($a) { $a }
258+
return (function ($a) {1}, $id, sum#1, [1], map{1:1}) ! .(1)
259+
};
260+
261+
declare
262+
%test:assertEquals(1,1,1,1,1)
263+
function bang:mixed-function-types-call-parenthesized () {
264+
let $id := function ($a) { $a }
265+
return (function ($a) {1}, $id, sum#1, [1], map{1:1}) ! (.(1))
266+
};
267+
268+
declare
269+
%test:assertEquals(1,1,1,1,1)
270+
function bang:mixed-function-types-call-parenthesized-context-item () {
271+
let $id := function ($a) { $a }
272+
return (function ($a) {1}, $id, sum#1, [1], map{1:1}) ! (.)(1)
273+
};
274+
275+
(:~
276+
: parse error needs to be fixed first
277+
: https://github.com/eXist-db/exist/issues/1655
278+
279+
declare
280+
%test:assertEquals(1,2,3)
281+
function bang:array-lookup-implicit-context () {
282+
([1], [2], [3]) ! ?1
283+
};
284+
285+
declare
286+
%test:assertEquals(1,2,3)
287+
function bang:map-lookup-implicit-context () {
288+
(map {'a':1}, map {'a':2}, map {'a':3}) ! ?a
289+
};
290+
291+
:)
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
(:
2+
: eXist-db Open Source Native XML Database
3+
: Copyright (C) 2001 The eXist-db Authors
4+
:
5+
6+
: http://www.exist-db.org
7+
:
8+
: This library is free software; you can redistribute it and/or
9+
: modify it under the terms of the GNU Lesser General Public
10+
: License as published by the Free Software Foundation; either
11+
: version 2.1 of the License, or (at your option) any later version.
12+
:
13+
: This library is distributed in the hope that it will be useful,
14+
: but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16+
: Lesser General Public License for more details.
17+
:
18+
: You should have received a copy of the GNU Lesser General Public
19+
: License along with this library; if not, write to the Free Software
20+
: Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21+
:)
22+
xquery version "3.1";
23+
24+
(:~
25+
: tests focussing on filtering function items in context
26+
:)
27+
module namespace filter-function-items-in-context="http://exist-db.org/xquery/test/filter-function-items-in-context";
28+
29+
30+
declare namespace test="http://exist-db.org/xquery/xqsuite";
31+
32+
33+
declare variable $filter-function-items-in-context:maps := ( map { "id": 1 }, map { "id": 2, "foo": 3 } );
34+
declare variable $filter-function-items-in-context:arrays := ( [1, 2], ["a", "b", "c"] );
35+
36+
declare
37+
%test:assertEquals(1)
38+
function filter-function-items-in-context:map-function() {
39+
$filter-function-items-in-context:maps[map:size(.) eq 1]?id
40+
};
41+
42+
declare
43+
%test:assertEquals(2)
44+
function filter-function-items-in-context:map-get() {
45+
$filter-function-items-in-context:maps[map:get(., "id") eq 2]?id
46+
};
47+
48+
declare
49+
%test:assertEquals(2)
50+
function filter-function-items-in-context:map-key() {
51+
$filter-function-items-in-context:maps[(.)("id") eq 2]?id
52+
};
53+
54+
declare
55+
%test:assertEquals(2)
56+
function filter-function-items-in-context:map-lookup() {
57+
$filter-function-items-in-context:maps[(.)?id eq 2]?id
58+
};
59+
60+
declare
61+
%test:assertEquals("a", "b", "c")
62+
function filter-function-items-in-context:array-get() {
63+
$filter-function-items-in-context:arrays[array:get(.,2) instance of xs:string]?*
64+
};
65+
66+
declare
67+
%test:assertEquals("a", "b", "c")
68+
function filter-function-items-in-context:array-lookup() {
69+
$filter-function-items-in-context:arrays[(.)?2 instance of xs:string]?*
70+
};
71+
72+
declare
73+
%test:assertEquals(1)
74+
function filter-function-items-in-context:array-function() {
75+
$filter-function-items-in-context:arrays[array:size(.) eq 2]?1
76+
};
77+
78+
declare
79+
%test:assertEquals(1)
80+
function filter-function-items-in-context:named-function-arity () {
81+
(
82+
current-date#0,
83+
string-join#1
84+
)
85+
[function-arity(.) eq 1] => count()
86+
};
87+
88+
declare
89+
%test:assertEquals(1)
90+
function filter-function-items-in-context:anonymous-function-arity () {
91+
(
92+
function() { "foo" },
93+
function($x) { "bar" }
94+
)
95+
[function-arity(.) eq 1] => count()
96+
};
97+
98+
declare
99+
%test:assertEquals(7)
100+
function filter-function-items-in-context:mixed-sequence () {
101+
(
102+
$filter-function-items-in-context:arrays,
103+
$filter-function-items-in-context:maps,
104+
string-join#1,
105+
function($x) { $x },
106+
sum#1
107+
)
108+
[. instance of function(*)] => count()
109+
};

0 commit comments

Comments
 (0)