• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

FontoXML / fontoxpath / 8751322661

19 Apr 2024 09:16AM UTC coverage: 91.581% (-0.01%) from 91.594%
8751322661

Pull #634

github

DrRataplan
Include Martin in the license for FontoXPath

As dicussed with Bert Willems.
Pull Request #634: Include Martin in the license for FontoXPath

4963 of 5763 branches covered (86.12%)

Branch coverage included in aggregate %.

10669 of 11306 relevant lines covered (94.37%)

97612.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.19
/src/parsing/compileAstToExpression.ts
1
import CurlyArrayConstructor from '../expressions/arrays/CurlyArrayConstructor';
15✔
2
import SquareArrayConstructor from '../expressions/arrays/SquareArrayConstructor';
15✔
3
import AncestorAxis from '../expressions/axes/AncestorAxis';
15✔
4
import AttributeAxis from '../expressions/axes/AttributeAxis';
15✔
5
import ChildAxis from '../expressions/axes/ChildAxis';
15✔
6
import DescendantAxis from '../expressions/axes/DescendantAxis';
15✔
7
import FollowingAxis from '../expressions/axes/FollowingAxis';
15✔
8
import FollowingSiblingAxis from '../expressions/axes/FollowingSiblingAxis';
15✔
9
import ParentAxis from '../expressions/axes/ParentAxis';
15✔
10
import PrecedingAxis from '../expressions/axes/PrecedingAxis';
15✔
11
import PrecedingSiblingAxis from '../expressions/axes/PrecedingSiblingAxis';
15✔
12
import SelfAxis from '../expressions/axes/SelfAxis';
15✔
13
import IfExpression from '../expressions/conditional/IfExpression';
15✔
14
import { SequenceMultiplicity, SequenceType, ValueType } from '../expressions/dataTypes/Value';
15
import QName from '../expressions/dataTypes/valueTypes/QName';
15✔
16
import StackTraceGenerator, { SourceRange } from '../expressions/debug/StackTraceGenerator';
15✔
17
import Expression, { RESULT_ORDERINGS } from '../expressions/Expression';
15✔
18
import FlworExpression from '../expressions/FlworExpression';
19
import ForExpression from '../expressions/ForExpression';
15✔
20
import FunctionCall from '../expressions/functions/FunctionCall';
15✔
21
import InlineFunction from '../expressions/functions/InlineFunction';
15✔
22
import LetExpression from '../expressions/LetExpression';
15✔
23
import Literal from '../expressions/literals/Literal';
15✔
24
import MapConstructor from '../expressions/maps/MapConstructor';
15✔
25
import NamedFunctionRef from '../expressions/NamedFunctionRef';
15✔
26
import BinaryOperator, {
15✔
27
        BinaryEvaluationFunction,
28
        generateBinaryOperatorFunction,
29
} from '../expressions/operators/arithmetic/BinaryOperator';
30
import Unary from '../expressions/operators/arithmetic/Unary';
15✔
31
import AndOperator from '../expressions/operators/boolean/AndOperator';
15✔
32
import OrOperator from '../expressions/operators/boolean/OrOperator';
15✔
33
import GeneralCompare from '../expressions/operators/compares/GeneralCompare';
15✔
34
import NodeCompare from '../expressions/operators/compares/NodeCompare';
15✔
35
import ValueCompare from '../expressions/operators/compares/ValueCompare';
15✔
36
import IntersectExcept from '../expressions/operators/IntersectExcept';
15✔
37
import SequenceOperator from '../expressions/operators/SequenceOperator';
15✔
38
import SimpleMapOperator from '../expressions/operators/SimpleMapOperator';
15✔
39
import CastableAsOperator from '../expressions/operators/types/CastableAsOperator';
15✔
40
import CastAsOperator from '../expressions/operators/types/CastAsOperator';
15✔
41
import InstanceOfOperator from '../expressions/operators/types/InstanceOfOperator';
15✔
42
import Union from '../expressions/operators/Union';
15✔
43
import OrderByExpression from '../expressions/OrderByExpression';
15✔
44
import AbsolutePathExpression from '../expressions/path/AbsolutePathExpression';
15✔
45
import ContextItemExpression from '../expressions/path/ContextItemExpression';
15✔
46
import PathExpression from '../expressions/path/PathExpression';
15✔
47
import PossiblyUpdatingExpression from '../expressions/PossiblyUpdatingExpression';
48
import Filter from '../expressions/postfix/Filter';
15✔
49
import Lookup from '../expressions/postfix/Lookup';
15✔
50
import UnaryLookup from '../expressions/postfix/UnaryLookup';
15✔
51
import QuantifiedExpression from '../expressions/quantified/QuantifiedExpression';
15✔
52
import KindTest from '../expressions/tests/KindTest';
15✔
53
import NameTest from '../expressions/tests/NameTest';
15✔
54
import PITest from '../expressions/tests/PITest';
15✔
55
import TestAbstractExpression from '../expressions/tests/TestAbstractExpression';
56
import TypeTest from '../expressions/tests/TypeTest';
15✔
57
import { Bucket, intersectBuckets } from '../expressions/util/Bucket';
15✔
58
import VarRef from '../expressions/VarRef';
15✔
59
import WhereExpression from '../expressions/WhereExpression';
15✔
60
import DeleteExpression from '../expressions/xquery-update/DeleteExpression';
15✔
61
import InsertExpression, { TargetChoice } from '../expressions/xquery-update/InsertExpression';
15✔
62
import RenameExpression from '../expressions/xquery-update/RenameExpression';
15✔
63
import ReplaceExpression from '../expressions/xquery-update/ReplaceExpression';
15✔
64
import TransformExpression from '../expressions/xquery-update/TransformExpression';
15✔
65
import AttributeConstructor from '../expressions/xquery/AttributeConstructor';
15✔
66
import CommentConstructor from '../expressions/xquery/CommentConstructor';
15✔
67
import ElementConstructor from '../expressions/xquery/ElementConstructor';
15✔
68
import PIConstructor from '../expressions/xquery/PIConstructor';
15✔
69
import TextConstructor from '../expressions/xquery/TextConstructor';
15✔
70
import TypeSwitchExpression from '../expressions/xquery/TypeSwitchExpression';
15✔
71
import astHelper, { IAST } from './astHelper';
15✔
72

73
const COMPILATION_OPTIONS = {
15✔
74
        XPATH_MODE: { allowXQuery: false, allowUpdating: false },
75
        XQUERY_MODE: { allowXQuery: true, allowUpdating: false },
76
        XQUERY_UPDATING_MODE: { allowXQuery: true, allowUpdating: true },
77
};
78

79
function disallowUpdating(compilationOptions: CompilationOptions) {
80
        if (!compilationOptions.allowXQuery) {
66,816✔
81
                return COMPILATION_OPTIONS.XPATH_MODE;
10,776✔
82
        }
83
        if (!compilationOptions.allowUpdating) {
56,040✔
84
                return COMPILATION_OPTIONS.XQUERY_MODE;
2,709✔
85
        }
86
        return COMPILATION_OPTIONS.XQUERY_UPDATING_MODE;
53,331✔
87
}
88

89
type CompilationOptions = { allowUpdating?: boolean; allowXQuery?: boolean };
90

91
function compile(ast: IAST, compilationOptions: CompilationOptions): Expression {
92
        const name = ast[0];
101,716✔
93
        switch (name) {
101,716!
94
                // Operators
95
                case 'andOp':
96
                        return andOp(ast, compilationOptions);
375✔
97
                case 'orOp':
98
                        return orOp(ast, compilationOptions);
63✔
99
                case 'unaryPlusOp':
100
                        return unaryPlus(ast, compilationOptions);
45✔
101
                case 'unaryMinusOp':
102
                        return unaryMinus(ast, compilationOptions);
120✔
103
                case 'addOp':
104
                case 'subtractOp':
105
                case 'multiplyOp':
106
                case 'divOp':
107
                case 'idivOp':
108
                case 'modOp':
109
                        return binaryOperator(ast, compilationOptions);
377✔
110
                case 'sequenceExpr':
111
                        return sequence(ast, compilationOptions);
3,720✔
112
                case 'unionOp':
113
                        return unionOp(ast, compilationOptions);
210✔
114
                case 'exceptOp':
115
                case 'intersectOp':
116
                        return intersectExcept(ast, compilationOptions);
78✔
117
                case 'stringConcatenateOp':
118
                        return stringConcatenateOp(ast, compilationOptions);
141✔
119
                case 'rangeSequenceExpr':
120
                        return rangeSequenceExpr(ast, compilationOptions);
102✔
121

122
                // Compares
123
                case 'equalOp':
124
                case 'notEqualOp':
125
                case 'lessThanOrEqualOp':
126
                case 'lessThanOp':
127
                case 'greaterThanOrEqualOp':
128
                case 'greaterThanOp':
129
                        return compare('generalCompare', ast, compilationOptions);
1,335✔
130
                case 'eqOp':
131
                case 'neOp':
132
                case 'ltOp':
133
                case 'leOp':
134
                case 'gtOp':
135
                case 'geOp':
136
                        return compare('valueCompare', ast, compilationOptions);
1,032✔
137
                case 'isOp':
138
                case 'nodeBeforeOp':
139
                case 'nodeAfterOp':
140
                        return compare('nodeCompare', ast, compilationOptions);
138✔
141

142
                // Path
143
                case 'pathExpr':
144
                        return pathExpr(ast, compilationOptions);
13,091✔
145
                case 'contextItemExpr':
146
                        const type = astHelper.getAttribute(ast, 'type');
819✔
147
                        return new ContextItemExpression(type);
819✔
148

149
                // Functions
150
                case 'functionCallExpr':
151
                        return functionCall(ast, compilationOptions);
8,471✔
152
                case 'inlineFunctionExpr':
153
                        return inlineFunction(ast, compilationOptions);
117✔
154
                case 'arrowExpr':
155
                        return arrowExpr(ast, compilationOptions);
297✔
156
                case 'dynamicFunctionInvocationExpr':
157
                        return dynamicFunctionInvocationExpr(ast, compilationOptions);
252✔
158
                case 'namedFunctionRef':
159
                        return namedFunctionRef(ast, compilationOptions);
108✔
160

161
                // Literals
162
                case 'integerConstantExpr':
163
                        return integerConstantExpr(ast, compilationOptions);
17,227✔
164
                case 'stringConstantExpr':
165
                        return stringConstantExpr(ast, compilationOptions);
15,531✔
166
                case 'decimalConstantExpr':
167
                        return decimalConstantExpr(ast, compilationOptions);
231✔
168
                case 'doubleConstantExpr':
169
                        return doubleConstantExpr(ast, compilationOptions);
90✔
170

171
                // Variables
172
                case 'varRef':
173
                        return varRef(ast, compilationOptions);
15,900✔
174
                case 'flworExpr':
175
                        return flworExpression(ast, compilationOptions);
3,600✔
176

177
                // Quantified
178
                case 'quantifiedExpr':
179
                        return quantified(ast, compilationOptions);
63✔
180

181
                // Conditional
182
                case 'ifThenElseExpr':
183
                        return IfThenElseExpr(ast, compilationOptions);
288✔
184

185
                case 'instanceOfExpr':
186
                        return instanceOf(ast, compilationOptions);
450✔
187
                case 'castExpr':
188
                        return castAs(ast, compilationOptions);
177✔
189
                case 'castableExpr':
190
                        return castableAs(ast, compilationOptions);
225✔
191

192
                case 'simpleMapExpr':
193
                        return simpleMap(ast, compilationOptions);
144✔
194

195
                case 'mapConstructor':
196
                        return mapConstructor(ast, compilationOptions);
471✔
197

198
                case 'arrayConstructor':
199
                        return arrayConstructor(ast, compilationOptions);
408✔
200

201
                case 'unaryLookup':
202
                        const returnType = astHelper.getAttribute(ast, 'type');
6✔
203
                        return new UnaryLookup(compileLookup(ast, compilationOptions), returnType);
6✔
204

205
                case 'typeswitchExpr':
206
                        return typeswitchExpr(ast, compilationOptions);
186✔
207

208
                // XQuery node constructors
209
                case 'elementConstructor':
210
                        return dirElementConstructor(ast, compilationOptions);
3,531✔
211
                case 'attributeConstructor':
212
                        return attributeConstructor(ast, compilationOptions);
939✔
213
                case 'computedAttributeConstructor':
214
                        return computedAttributeConstructor(ast, compilationOptions);
1,350✔
215
                case 'computedCommentConstructor':
216
                        return computedCommentConstructor(ast, compilationOptions);
453✔
217
                case 'computedTextConstructor':
218
                        return computedTextConstructor(ast, compilationOptions);
429✔
219
                case 'computedElementConstructor':
220
                        return computedElementConstructor(ast, compilationOptions);
×
221
                case 'computedPIConstructor':
222
                        return computedPIConstructor(ast, compilationOptions);
279✔
223
                case 'CDataSection':
224
                        return CDataSection(ast, compilationOptions);
9✔
225

226
                // XQuery update facility
227
                case 'deleteExpr':
228
                        return deleteExpression(ast, compilationOptions);
1,284✔
229
                case 'insertExpr':
230
                        return insertExpression(ast, compilationOptions);
1,890✔
231
                case 'renameExpr':
232
                        return renameExpression(ast, compilationOptions);
843✔
233
                case 'replaceExpr':
234
                        return replaceExpression(ast, compilationOptions);
2,052✔
235
                case 'transformExpr':
236
                        return transformExpression(ast, compilationOptions);
606✔
237

238
                case 'x:stackTrace':
239
                        return stackTrace(ast, compilationOptions);
462✔
240

241
                // Some simple aliases:
242
                case 'ifClause':
243
                case 'thenClause':
244
                case 'elseClause':
245
                        return compile(astHelper.getFirstChild(ast, '*'), compilationOptions);
864✔
246

247
                default:
248
                        return compileTest(ast, compilationOptions);
837✔
249
        }
250
}
251

252
function compileTest(ast: IAST, compilationOptions: CompilationOptions): TestAbstractExpression {
253
        switch (ast[0]) {
21,251!
254
                // Tests
255
                case 'nameTest':
256
                        return nameTest(ast, compilationOptions);
16,657✔
257
                case 'piTest':
258
                        return piTest(ast, compilationOptions);
343✔
259
                case 'commentTest':
260
                        return commentTest(ast, compilationOptions);
297✔
261
                case 'textTest':
262
                        return textTest(ast, compilationOptions);
534✔
263
                case 'documentTest':
264
                        return documentTest(ast, compilationOptions);
3✔
265
                case 'attributeTest':
266
                        return attributeTest(ast, compilationOptions);
57✔
267
                case 'elementTest':
268
                        return elementTest(ast, compilationOptions);
134✔
269
                case 'anyKindTest':
270
                        return anyKindTest();
1,770✔
271
                case 'anyMapTest':
272
                        return anyMapTest(ast, compilationOptions);
3✔
273
                case 'anyArrayTest':
274
                        return anyArrayTest(ast, compilationOptions);
×
275
                case 'Wildcard':
276
                        return wildcard(ast, compilationOptions);
664✔
277
                case 'atomicType':
278
                        return typeTest(ast, compilationOptions);
789✔
279
                case 'anyItemType':
280
                        return anyItemTest();
×
281

282
                default:
283
                        throw new Error('No selector counterpart for: ' + ast[0] + '.');
×
284
        }
285
}
286

287
function stackTrace(ast: IAST, compilationOptions: CompilationOptions) {
288
        const innerExpression = ast[2] as IAST;
462✔
289

290
        // Do not make nested stacktraces at the same exact location. Just use the inner one
291
        let nextCompilableExpression: IAST = innerExpression;
462✔
292
        while (nextCompilableExpression[0] === 'x:stackTrace') {
462✔
293
                ast = nextCompilableExpression;
393✔
294
                nextCompilableExpression = nextCompilableExpression[2] as IAST;
393✔
295
        }
296

297
        const location = ast[1] as SourceRange;
462✔
298

299
        return new StackTraceGenerator(
462✔
300
                location,
301
                nextCompilableExpression[0],
302
                compile(nextCompilableExpression, compilationOptions),
303
                location.comment,
304
        );
305
}
306

307
function arrayConstructor(ast: IAST, compilationOptions: CompilationOptions) {
308
        const type = astHelper.getAttribute(ast, 'type');
408✔
309
        const arrConstructor = astHelper.getFirstChild(ast, '*');
408✔
310
        const members = astHelper
408✔
311
                .getChildren(arrConstructor, 'arrayElem')
312
                .map((arrayElem) =>
313
                        compile(astHelper.getFirstChild(arrayElem, '*'), disallowUpdating(compilationOptions)),
1,050✔
314
                );
315
        switch (arrConstructor[0]) {
408!
316
                case 'curlyArray':
317
                        return new CurlyArrayConstructor(members, type);
45✔
318
                case 'squareArray':
319
                        return new SquareArrayConstructor(members, type);
363✔
320
                default:
321
                        throw new Error('Unrecognized arrayType: ' + arrConstructor[0]);
×
322
        }
323
}
324

325
function mapConstructor(ast: IAST, compilationOptions: CompilationOptions) {
326
        const type = astHelper.getAttribute(ast, 'type');
471✔
327
        return new MapConstructor(
471✔
328
                astHelper.getChildren(ast, 'mapConstructorEntry').map((keyValuePair) => ({
699✔
329
                        key: compile(
330
                                astHelper.followPath(keyValuePair, ['mapKeyExpr', '*']),
331
                                disallowUpdating(compilationOptions),
332
                        ),
333
                        value: compile(
334
                                astHelper.followPath(keyValuePair, ['mapValueExpr', '*']),
335
                                disallowUpdating(compilationOptions),
336
                        ),
337
                })),
338
                type,
339
        );
340
}
341

342
function unwrapBinaryOperator(
343
        operatorName: string,
344
        ast: IAST,
345
        compilationOptions: CompilationOptions,
346
) {
347
        const compiledAstNodes: Expression[] = [];
438✔
348
        function unwrapInner(innerAst: IAST) {
349
                const firstOperand = astHelper.getFirstChild(
462✔
350
                        astHelper.getFirstChild(innerAst, 'firstOperand') as IAST,
351
                        '*',
352
                ) as IAST;
353
                const secondOperand = astHelper.getFirstChild(
462✔
354
                        astHelper.getFirstChild(innerAst, 'secondOperand') as IAST,
355
                        '*',
356
                ) as IAST;
357

358
                if (firstOperand[0] === operatorName) {
462✔
359
                        unwrapInner(firstOperand);
24✔
360
                } else {
361
                        compiledAstNodes.push(compile(firstOperand as IAST, compilationOptions));
438✔
362
                }
363
                if (secondOperand[0] === operatorName) {
462!
364
                        unwrapInner(secondOperand);
×
365
                } else {
366
                        compiledAstNodes.push(compile(secondOperand as IAST, compilationOptions));
462✔
367
                }
368
        }
369
        unwrapInner(ast);
438✔
370

371
        return compiledAstNodes;
438✔
372
}
373

374
function andOp(ast: IAST, compilationOptions: CompilationOptions) {
375
        const type = astHelper.getAttribute(ast, 'type');
375✔
376
        return new AndOperator(
375✔
377
                unwrapBinaryOperator('andOp', ast, disallowUpdating(compilationOptions)),
378
                type,
379
        );
380
}
381

382
function orOp(ast: IAST, compilationOptions: CompilationOptions) {
383
        const type = astHelper.getAttribute(ast, 'type');
63✔
384
        return new OrOperator(
63✔
385
                unwrapBinaryOperator('orOp', ast, disallowUpdating(compilationOptions)),
386
                type,
387
        );
388
}
389

390
function binaryOperator(ast: IAST, compilationOptions: CompilationOptions) {
391
        const kind = ast[0];
377✔
392
        const a = compile(
377✔
393
                astHelper.followPath(ast, ['firstOperand', '*']),
394
                disallowUpdating(compilationOptions),
395
        );
396
        const b = compile(
377✔
397
                astHelper.followPath(ast, ['secondOperand', '*']),
398
                disallowUpdating(compilationOptions),
399
        );
400

401
        const attributeType = astHelper.getAttribute(ast, 'type');
377✔
402
        const first = astHelper.getAttribute(astHelper.followPath(ast, ['firstOperand', '*']), 'type');
377✔
403
        const second = astHelper.getAttribute(
377✔
404
                astHelper.followPath(ast, ['secondOperand', '*']),
405
                'type',
406
        );
407

408
        let evaluateFunction: BinaryEvaluationFunction;
409
        if (first && second && astHelper.getAttribute(ast, 'type')) {
377✔
410
                evaluateFunction = generateBinaryOperatorFunction(kind, first.type, second.type);
212✔
411
        }
412

413
        return new BinaryOperator(kind, a, b, attributeType, evaluateFunction);
377✔
414
}
415

416
function compileLookup(ast: IAST, compilationOptions: CompilationOptions): '*' | Expression {
417
        const keyExpression = astHelper.getFirstChild(ast, '*');
81✔
418
        switch (keyExpression[0]) {
81✔
419
                case 'NCName':
420
                        return new Literal(astHelper.getTextContent(keyExpression), {
33✔
421
                                type: ValueType.XSSTRING,
422
                                mult: SequenceMultiplicity.EXACTLY_ONE,
423
                        });
424
                case 'star':
425
                        return '*';
24✔
426
                default:
427
                        return compile(keyExpression, disallowUpdating(compilationOptions));
24✔
428
        }
429
}
430

431
function castAs(ast: IAST, compilationOptions: CompilationOptions) {
432
        const expression = compile(
177✔
433
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'argExpr'), '*'),
434
                disallowUpdating(compilationOptions),
435
        );
436

437
        const singleType = astHelper.getFirstChild(ast, 'singleType');
177✔
438
        const targetType = astHelper.getQName(astHelper.getFirstChild(singleType, 'atomicType'));
177✔
439
        const optional = astHelper.getFirstChild(singleType, 'optional') !== null;
177✔
440

441
        return new CastAsOperator(expression, targetType, optional);
177✔
442
}
443

444
function castableAs(ast: IAST, compilationOptions: CompilationOptions) {
445
        const expression = compile(
225✔
446
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'argExpr'), '*'),
447
                disallowUpdating(compilationOptions),
448
        );
449

450
        const singleType = astHelper.getFirstChild(ast, 'singleType');
225✔
451
        const targetType = astHelper.getQName(astHelper.getFirstChild(singleType, 'atomicType'));
225✔
452
        const optional = astHelper.getFirstChild(singleType, 'optional') !== null;
225✔
453

454
        return new CastableAsOperator(expression, targetType, optional);
225✔
455
}
456

457
// Binary compare (=, !=, le, is, <<, >>, etc)
458
function compare(compareType: string, ast: IAST, compilationOptions: CompilationOptions) {
459
        const firstOperand = astHelper.followPath(ast, ['firstOperand', '*']);
2,505✔
460
        const secondOperand = astHelper.followPath(ast, ['secondOperand', '*']);
2,505✔
461

462
        const firstExpression = compile(firstOperand, disallowUpdating(compilationOptions));
2,505✔
463
        const secondExpression = compile(secondOperand, disallowUpdating(compilationOptions));
2,505✔
464

465
        switch (compareType) {
2,505✔
466
                case 'valueCompare':
467
                        return new ValueCompare(ast[0], firstExpression, secondExpression);
1,032✔
468
                case 'nodeCompare':
469
                        return new NodeCompare(ast[0], firstExpression, secondExpression);
138✔
470
                case 'generalCompare':
471
                        return new GeneralCompare(ast[0], firstExpression, secondExpression);
1,335✔
472
        }
473
}
474

475
function IfThenElseExpr(ast: IAST, compilationOptions: CompilationOptions) {
476
        const retType = astHelper.getAttribute(ast, 'type');
288✔
477
        const ifClausePart =
478
                astHelper.getFirstChild(ast, 'ifClause') || astHelper.getChildren(ast, 'x:stackTrace')[0];
288✔
479
        const thenClausePart =
480
                astHelper.getFirstChild(ast, 'thenClause') || astHelper.getChildren(ast, 'x:stackTrace')[1];
288✔
481
        const elseClausePart =
482
                astHelper.getFirstChild(ast, 'elseClause') || astHelper.getChildren(ast, 'x:stackTrace')[2];
288✔
483

484
        return new IfExpression(
288✔
485
                compile(ifClausePart, disallowUpdating(compilationOptions)),
486
                compile(thenClausePart, compilationOptions) as PossiblyUpdatingExpression,
487
                compile(elseClausePart, compilationOptions) as PossiblyUpdatingExpression,
488
                retType,
489
        );
490
}
491

492
function forClause(
493
        expressionClause: IAST,
494
        compilationOptions: CompilationOptions,
495
        returnClauseExpression: PossiblyUpdatingExpression | FlworExpression,
496
): ForExpression {
497
        const forClauseItems = astHelper.getChildren(expressionClause, '*');
276✔
498
        let returnExpr = returnClauseExpression;
276✔
499

500
        for (let i = forClauseItems.length - 1; i >= 0; --i) {
276✔
501
                const forClauseItem = forClauseItems[i];
315✔
502
                const expression = astHelper.followPath(forClauseItem, ['forExpr', '*']);
315✔
503
                const positionalVariableBinding = astHelper.getFirstChild(
315✔
504
                        forClauseItem,
505
                        'positionalVariableBinding',
506
                );
507

508
                returnExpr = new ForExpression(
315✔
509
                        astHelper.getQName(
510
                                astHelper.followPath(forClauseItem, ['typedVariableBinding', 'varName']),
511
                        ),
512
                        compile(expression, disallowUpdating(compilationOptions)),
513
                        positionalVariableBinding ? astHelper.getQName(positionalVariableBinding) : null,
315✔
514
                        returnExpr,
515
                );
516
        }
517

518
        return returnExpr as ForExpression;
276✔
519
}
520

521
function letClause(
522
        expressionClause: IAST,
523
        compilationOptions: CompilationOptions,
524
        returnClauseExpression: PossiblyUpdatingExpression | FlworExpression,
525
): LetExpression {
526
        const letClauseItems = astHelper.getChildren(expressionClause, '*');
4,401✔
527
        let returnExpr = returnClauseExpression;
4,401✔
528

529
        for (let i = letClauseItems.length - 1; i >= 0; --i) {
4,401✔
530
                const letClauseItem = letClauseItems[i];
4,461✔
531
                const expression = astHelper.followPath(letClauseItem, ['letExpr', '*']);
4,461✔
532
                returnExpr = new LetExpression(
4,461✔
533
                        astHelper.getQName(
534
                                astHelper.followPath(letClauseItem, ['typedVariableBinding', 'varName']),
535
                        ),
536
                        compile(expression, disallowUpdating(compilationOptions)),
537
                        returnExpr,
538
                );
539
        }
540

541
        return returnExpr as LetExpression;
4,401✔
542
}
543

544
function whereClause(
545
        expressionClause: IAST,
546
        compilationOptions: CompilationOptions,
547
        returnClauseExpression: PossiblyUpdatingExpression | FlworExpression,
548
): WhereExpression {
549
        const whereClauseItems = astHelper.getChildren(expressionClause, '*');
66✔
550
        let returnExpr = returnClauseExpression;
66✔
551

552
        for (let i = whereClauseItems.length - 1; i >= 0; --i) {
66✔
553
                const whereClauseItem = whereClauseItems[i];
66✔
554
                returnExpr = new WhereExpression(compile(whereClauseItem, compilationOptions), returnExpr);
66✔
555
        }
556

557
        return returnExpr as WhereExpression;
66✔
558
}
559

560
function orderByClause(
561
        expressionClause: IAST,
562
        compilationOptions: CompilationOptions,
563
        returnClauseExpression: PossiblyUpdatingExpression,
564
): OrderByExpression {
565
        const orderBySpecs = astHelper.getChildren(expressionClause, '*');
33✔
566
        return new OrderByExpression(
33✔
567
                orderBySpecs
568
                        .filter((spec) => spec[0] !== 'stable')
33✔
569
                        .map((spec) => {
570
                                const orderModifier = astHelper.getFirstChild(spec, 'orderModifier');
33✔
571
                                const kind = orderModifier
33✔
572
                                        ? astHelper.getFirstChild(orderModifier, 'orderingKind')
573
                                        : null;
574
                                const mode = orderModifier
33✔
575
                                        ? astHelper.getFirstChild(orderModifier, 'emptyOrderingMode')
576
                                        : null;
577

578
                                const isAscending = kind ? astHelper.getTextContent(kind) === 'ascending' : true;
33✔
579
                                const isEmptyLeast = mode ? astHelper.getTextContent(mode) === 'empty least' : true;
33!
580

581
                                return {
33✔
582
                                        expression: compile(
583
                                                astHelper.followPath(spec, ['orderByExpr', '*']),
584
                                                compilationOptions,
585
                                        ),
586
                                        isAscending,
587
                                        isEmptyLeast,
588
                                };
589
                        }),
590
                returnClauseExpression,
591
        );
592
}
593

594
function flworExpression(ast: IAST, compilationOptions: CompilationOptions) {
595
        const clausesAndReturnClause = astHelper.getChildren(ast, '*');
3,600✔
596
        const returnClauseExpression = astHelper.getFirstChild(
3,600✔
597
                clausesAndReturnClause[clausesAndReturnClause.length - 1],
598
                '*',
599
        );
600

601
        // Return intermediate and initial clauses handling
602
        const clauses = clausesAndReturnClause.slice(0, -1);
3,600✔
603

604
        // We have to check if there are any intermediate clauses before compiling them.
605
        if (clauses.length > 1) {
3,600✔
606
                if (!compilationOptions.allowXQuery) {
390!
607
                        throw new Error('XPST0003: Use of XQuery FLWOR expressions in XPath is no allowed');
×
608
                }
609
        }
610

611
        return clauses.reduceRight(
3,600✔
612
                (returnOfPreviousExpression: PossiblyUpdatingExpression, flworExpressionClause: IAST) => {
613
                        switch (flworExpressionClause[0]) {
4,776!
614
                                case 'forClause':
615
                                        return forClause(
276✔
616
                                                flworExpressionClause,
617
                                                compilationOptions,
618
                                                returnOfPreviousExpression,
619
                                        );
620
                                case 'letClause':
621
                                        return letClause(
4,401✔
622
                                                flworExpressionClause,
623
                                                compilationOptions,
624
                                                returnOfPreviousExpression,
625
                                        );
626
                                case 'whereClause':
627
                                        return whereClause(
66✔
628
                                                flworExpressionClause,
629
                                                compilationOptions,
630
                                                returnOfPreviousExpression,
631
                                        );
632
                                case 'windowClause':
633
                                        throw new Error(
×
634
                                                `Not implemented: ${flworExpressionClause[0]} is not implemented yet.`,
635
                                        );
636
                                case 'groupByClause':
637
                                        throw new Error(
×
638
                                                `Not implemented: ${flworExpressionClause[0]} is not implemented yet.`,
639
                                        );
640
                                case 'orderByClause':
641
                                        return orderByClause(
33✔
642
                                                flworExpressionClause,
643
                                                compilationOptions,
644
                                                returnOfPreviousExpression,
645
                                        );
646
                                case 'countClause':
647
                                        throw new Error(
×
648
                                                `Not implemented: ${flworExpressionClause[0]} is not implemented yet.`,
649
                                        );
650
                                default:
651
                                        throw new Error(
×
652
                                                `Not implemented: ${flworExpressionClause[0]} is not supported in a flwor expression`,
653
                                        );
654
                        }
655
                },
656
                compile(returnClauseExpression, compilationOptions),
657
        );
658
}
659

660
function functionCall(
661
        ast: IAST,
662
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
663
) {
664
        const functionName = astHelper.getFirstChild(ast, 'functionName');
8,471✔
665
        const functionArguments = astHelper.getChildren(astHelper.getFirstChild(ast, 'arguments'), '*');
8,471✔
666

667
        const returnType = astHelper.getAttribute(ast, 'type');
8,471✔
668

669
        return new FunctionCall(
8,471✔
670
                new NamedFunctionRef(
671
                        astHelper.getQName(functionName),
672
                        functionArguments.length,
673
                        returnType,
674
                ),
675
                functionArguments.map((arg) =>
676
                        arg[0] === 'argumentPlaceholder' ? null : compile(arg, compilationOptions),
10,215✔
677
                ),
678
                returnType,
679
        );
680
}
681

682
function arrowExpr(ast: IAST, compilationOptions: CompilationOptions) {
683
        const type = astHelper.getAttribute(ast, 'type');
297✔
684
        const argExpr = astHelper.followPath(ast, ['argExpr', '*']);
297✔
685
        // Each part an EQName, expression, or arguments passed to the previous part
686
        const parts = astHelper.getChildren(ast, '*').slice(1);
297✔
687

688
        let args = [compile(argExpr, compilationOptions)];
297✔
689
        for (let i = 0; i < parts.length; i++) {
297✔
690
                if (parts[i][0] === 'arguments') {
594✔
691
                        continue;
297✔
692
                }
693
                if (parts[i + 1][0] === 'arguments') {
297!
694
                        const functionArguments = astHelper.getChildren(parts[i + 1], '*');
297✔
695
                        args = args.concat(
297✔
696
                                functionArguments.map((arg) =>
697
                                        arg[0] === 'argumentPlaceholder' ? null : compile(arg, compilationOptions),
63!
698
                                ),
699
                        );
700
                }
701

702
                const func =
703
                        parts[i][0] === 'EQName'
297✔
704
                                ? new NamedFunctionRef(astHelper.getQName(parts[i]), args.length, type)
705
                                : compile(parts[i], disallowUpdating(compilationOptions));
706
                args = [new FunctionCall(func, args, type)];
297✔
707
        }
708
        return args[0];
297✔
709
}
710

711
function dynamicFunctionInvocationExpr(ast: IAST, compilationOptions: CompilationOptions) {
712
        const functionItemContent = astHelper.followPath(ast, ['functionItem', '*']);
252✔
713
        const retType = astHelper.getAttribute(ast, 'type');
252✔
714
        const argumentsAst = astHelper.getFirstChild(ast, 'arguments');
252✔
715
        let args: Expression[] = [];
252✔
716
        if (argumentsAst) {
252✔
717
                const functionArguments = astHelper.getChildren(argumentsAst, '*');
219✔
718
                args = functionArguments.map((arg) =>
219✔
719
                        arg[0] === 'argumentPlaceholder' ? null : compile(arg, compilationOptions),
297✔
720
                );
721
        }
722

723
        return new FunctionCall(compile(functionItemContent, compilationOptions), args, retType);
252✔
724
}
725

726
function namedFunctionRef(ast: IAST, _compilationOptions: CompilationOptions) {
727
        const functionName = astHelper.getFirstChild(ast, 'functionName');
108✔
728
        const type = astHelper.getAttribute(ast, 'type');
108✔
729
        const arity = astHelper.getTextContent(
108✔
730
                astHelper.followPath(ast, ['integerConstantExpr', 'value']),
731
        );
732
        return new NamedFunctionRef(astHelper.getQName(functionName), parseInt(arity, 10), type);
108✔
733
}
734

735
function inlineFunction(
736
        ast: IAST,
737
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
738
) {
739
        const params = astHelper.getChildren(astHelper.getFirstChild(ast, 'paramList'), '*');
117✔
740
        const functionBody = astHelper.followPath(ast, ['functionBody', '*']);
117✔
741
        const type = astHelper.getAttribute(ast, 'type');
117✔
742

743
        return new InlineFunction(
117✔
744
                params.map((param) => {
745
                        const td: {
746
                                name: QName;
747
                                type: SequenceType;
748
                        } = {
126✔
749
                                name: astHelper.getQName(astHelper.getFirstChild(param, 'varName')),
750
                                type: astHelper.getTypeDeclaration(param),
751
                        };
752
                        return td;
126✔
753
                }),
754
                astHelper.getTypeDeclaration(ast),
755
                functionBody
117!
756
                        ? (compile(functionBody, compilationOptions) as PossiblyUpdatingExpression)
757
                        : new SequenceOperator([], type),
758
        );
759
}
760

761
function instanceOf(
762
        ast: IAST,
763
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
764
) {
765
        const expression = compile(astHelper.followPath(ast, ['argExpr', '*']), compilationOptions);
450✔
766
        const sequenceType = astHelper.followPath(ast, ['sequenceType', '*']);
450✔
767
        const occurrence = astHelper.followPath(ast, ['sequenceType', 'occurrenceIndicator']);
450✔
768
        const type = astHelper.getAttribute(ast, 'type');
450✔
769

770
        return new InstanceOfOperator(
450✔
771
                expression,
772
                compile(sequenceType, disallowUpdating(compilationOptions)),
773
                occurrence ? astHelper.getTextContent(occurrence) : '',
450✔
774
                type,
775
        );
776
}
777

778
function integerConstantExpr(ast: IAST, _compilationOptions: CompilationOptions) {
779
        return new Literal(astHelper.getTextContent(astHelper.getFirstChild(ast, 'value')), {
17,227✔
780
                type: ValueType.XSINTEGER,
781
                mult: SequenceMultiplicity.EXACTLY_ONE,
782
        });
783
}
784

785
function stringConstantExpr(ast: IAST, _compilationOptions: CompilationOptions) {
786
        return new Literal(astHelper.getTextContent(astHelper.getFirstChild(ast, 'value')), {
15,531✔
787
                type: ValueType.XSSTRING,
788
                mult: SequenceMultiplicity.EXACTLY_ONE,
789
        });
790
}
791

792
function decimalConstantExpr(ast: IAST, _compilationOptions: CompilationOptions) {
793
        return new Literal(astHelper.getTextContent(astHelper.getFirstChild(ast, 'value')), {
231✔
794
                type: ValueType.XSDECIMAL,
795
                mult: SequenceMultiplicity.EXACTLY_ONE,
796
        });
797
}
798

799
function doubleConstantExpr(ast: IAST, _compilationOptions: CompilationOptions) {
800
        return new Literal(astHelper.getTextContent(astHelper.getFirstChild(ast, 'value')), {
90✔
801
                type: ValueType.XSDOUBLE,
802
                mult: SequenceMultiplicity.EXACTLY_ONE,
803
        });
804
}
805

806
function nameTest(ast: IAST, _compilationOptions: CompilationOptions) {
807
        return new NameTest(astHelper.getQName(ast));
16,657✔
808
}
809

810
function anyKindTest() {
811
        return new TypeTest({ prefix: '', namespaceURI: null, localName: 'node()' });
1,770✔
812
}
813

814
function anyItemTest() {
815
        return new TypeTest({ prefix: '', namespaceURI: null, localName: 'item()' });
×
816
}
817

818
function pathExpr(ast: IAST, compilationOptions: CompilationOptions) {
819
        const type = astHelper.getAttribute(ast, 'type');
13,091✔
820
        const rawSteps = astHelper.getChildren(ast, 'stepExpr');
13,091✔
821
        let hasAxisStep = false;
13,091✔
822
        const steps = rawSteps.map((step) => {
13,091✔
823
                const axis = astHelper.getFirstChild(step, 'xpathAxis');
31,100✔
824

825
                let stepExpression: Expression;
826

827
                const children = astHelper.getChildren(step, '*');
31,100✔
828
                const postFixExpressions: (['lookup', '*' | Expression] | ['predicate', Expression])[] = [];
31,100✔
829
                let intersectingBucket: Bucket = null;
31,100✔
830
                let hasSeenNullBucket = false;
31,100✔
831
                for (const child of children) {
31,100✔
832
                        switch (child[0]) {
63,292✔
833
                                case 'lookup':
834
                                        postFixExpressions.push(['lookup', compileLookup(child, compilationOptions)]);
75✔
835
                                        break;
75✔
836
                                case 'predicate':
837
                                case 'predicates':
838
                                        for (const childPredicate of astHelper.getChildren(child, '*')) {
11,703✔
839
                                                const predicateExpression = compile(
11,757✔
840
                                                        childPredicate,
841
                                                        disallowUpdating(compilationOptions),
842
                                                );
843
                                                if (!hasSeenNullBucket) {
11,757✔
844
                                                        const predicateBucket = predicateExpression.getBucket();
11,715✔
845
                                                        if (predicateBucket === null) {
11,715✔
846
                                                                // If we see a null bucket, we cannot use any future buckets
847
                                                                // anymore. This filter may have been position-aware For example:
848
                                                                // `following-sibling::node()[1][self::xxx]` is not the same as
849
                                                                // `following-sibling::node()[self::xxx][1]`.
850
                                                                hasSeenNullBucket = true;
11,643✔
851
                                                        } else {
852
                                                                intersectingBucket = intersectBuckets(
72✔
853
                                                                        intersectingBucket,
854
                                                                        predicateBucket,
855
                                                                );
856
                                                        }
857
                                                }
858
                                                postFixExpressions.push(['predicate', predicateExpression]);
11,757✔
859
                                        }
860
                                        break;
11,703✔
861
                        }
862
                }
863

864
                if (axis) {
31,100✔
865
                        hasAxisStep = true;
20,414✔
866
                        const test = astHelper.getFirstChild(step, [
20,414✔
867
                                'attributeTest',
868
                                'anyElementTest',
869
                                'piTest',
870
                                'documentTest',
871
                                'elementTest',
872
                                'commentTest',
873
                                'namespaceTest',
874
                                'anyKindTest',
875
                                'textTest',
876
                                'anyFunctionTest',
877
                                'typedFunctionTest',
878
                                'schemaAttributeTest',
879
                                'atomicType',
880
                                'anyItemType',
881
                                'parenthesizedItemType',
882
                                'typedMapTest',
883
                                'typedArrayTest',
884
                                'nameTest',
885
                                'Wildcard',
886
                        ]);
887

888
                        const testExpression = compileTest(test, disallowUpdating(compilationOptions));
20,414✔
889
                        switch (astHelper.getTextContent(axis)) {
20,414✔
890
                                case 'ancestor':
891
                                        stepExpression = new AncestorAxis(testExpression, { inclusive: false });
30✔
892
                                        break;
30✔
893
                                case 'ancestor-or-self':
894
                                        stepExpression = new AncestorAxis(testExpression, { inclusive: true });
18✔
895
                                        break;
18✔
896
                                case 'attribute':
897
                                        stepExpression = new AttributeAxis(testExpression, intersectingBucket);
1,899✔
898
                                        break;
1,899✔
899
                                case 'child':
900
                                        stepExpression = new ChildAxis(testExpression, intersectingBucket);
15,882✔
901
                                        break;
15,882✔
902
                                case 'descendant':
903
                                        stepExpression = new DescendantAxis(testExpression, { inclusive: false });
138✔
904
                                        break;
138✔
905
                                case 'descendant-or-self':
906
                                        stepExpression = new DescendantAxis(testExpression, { inclusive: true });
1,419✔
907
                                        break;
1,419✔
908
                                case 'parent':
909
                                        stepExpression = new ParentAxis(testExpression, intersectingBucket);
138✔
910
                                        break;
138✔
911
                                case 'following-sibling':
912
                                        stepExpression = new FollowingSiblingAxis(testExpression, intersectingBucket);
93✔
913
                                        break;
93✔
914
                                case 'preceding-sibling':
915
                                        stepExpression = new PrecedingSiblingAxis(testExpression, intersectingBucket);
183✔
916
                                        break;
183✔
917
                                case 'following':
918
                                        stepExpression = new FollowingAxis(testExpression);
51✔
919
                                        break;
51✔
920
                                case 'preceding':
921
                                        stepExpression = new PrecedingAxis(testExpression);
57✔
922
                                        break;
57✔
923
                                case 'self':
924
                                        stepExpression = new SelfAxis(testExpression, intersectingBucket);
506✔
925
                                        break;
506✔
926
                        }
927
                } else {
928
                        // We must be a filter expression
929
                        const filterExpr = astHelper.followPath(step, ['filterExpr', '*']);
10,686✔
930
                        stepExpression = compile(filterExpr, disallowUpdating(compilationOptions));
10,686✔
931
                }
932

933
                for (const postfix of postFixExpressions) {
31,100✔
934
                        switch (postfix[0]) {
11,832✔
935
                                case 'lookup':
936
                                        stepExpression = new Lookup(stepExpression, postfix[1]);
75✔
937
                                        break;
75✔
938
                                case 'predicate':
939
                                        stepExpression = new Filter(stepExpression, postfix[1]);
11,757✔
940
                                        break;
11,757✔
941
                        }
942
                }
943

944
                stepExpression.type = type;
31,100✔
945
                return stepExpression;
31,100✔
946
        });
947

948
        const isAbsolute = astHelper.getFirstChild(ast, 'rootExpr');
13,091✔
949
        // If an path has no axis steps, we should skip sorting. The path
950
        // is probably a chain of filter expressions or lookups
951
        const requireSorting = hasAxisStep || isAbsolute !== null || rawSteps.length > 1;
13,091✔
952

953
        // Directly use expressions which are not path expression
954
        if (!requireSorting && steps.length === 1) {
13,091✔
955
                return steps[0];
732✔
956
        }
957

958
        // We do not have to sort the result of steps expressions when
959
        // they already result to a ordered set
960
        if (
12,359✔
961
                !isAbsolute &&
26,211✔
962
                steps.length === 1 &&
963
                steps[0].expectedResultOrder === RESULT_ORDERINGS.SORTED
964
        ) {
965
                return steps[0];
1,166✔
966
        }
967

968
        if (isAbsolute && steps.length === 0) {
11,193✔
969
                return new AbsolutePathExpression(null);
48✔
970
        }
971
        const pathExpression = new PathExpression(steps, requireSorting);
11,145✔
972
        if (isAbsolute) {
11,145✔
973
                return new AbsolutePathExpression(pathExpression);
441✔
974
        }
975
        return pathExpression;
10,704✔
976
}
977

978
function piTest(ast: IAST, _compilationOptions: CompilationOptions) {
979
        const piTarget = astHelper.getFirstChild(ast, 'piTarget');
343✔
980
        if (piTarget) {
343✔
981
                return new PITest(astHelper.getTextContent(piTarget));
30✔
982
        }
983
        return new KindTest(7);
313✔
984
}
985

986
function commentTest(_ast: IAST, _compilationOptions: CompilationOptions) {
987
        return new KindTest(8);
297✔
988
}
989

990
function documentTest(_ast: IAST, _compilationOptions: CompilationOptions) {
991
        return new KindTest(9);
3✔
992
}
993

994
function elementTest(ast: IAST, _compilationOptions: CompilationOptions) {
995
        const elementName = astHelper.getFirstChild(ast, 'elementName');
134✔
996
        const star = elementName && astHelper.getFirstChild(elementName, 'star');
134✔
997
        if (!elementName || star) {
134✔
998
                return new KindTest(1);
125✔
999
        }
1000
        return new NameTest(astHelper.getQName(astHelper.getFirstChild(elementName, 'QName')), {
9✔
1001
                kind: 1,
1002
        });
1003
}
1004

1005
function attributeTest(ast: IAST, _compilationOptions: CompilationOptions) {
1006
        const attributeName = astHelper.getFirstChild(ast, 'attributeName');
57✔
1007
        const star = attributeName && astHelper.getFirstChild(attributeName, 'star');
57✔
1008
        if (!attributeName || star) {
57✔
1009
                return new KindTest(2);
51✔
1010
        }
1011
        return new NameTest(astHelper.getQName(astHelper.getFirstChild(attributeName, 'QName')), {
6✔
1012
                kind: 2,
1013
        });
1014
}
1015

1016
function textTest(_ast: IAST, _compilationOptions: CompilationOptions) {
1017
        return new KindTest(3);
534✔
1018
}
1019

1020
function quantified(ast: IAST, compilationOptions: CompilationOptions) {
1021
        const type = astHelper.getAttribute(ast, 'type');
63✔
1022
        const quantifier = astHelper.getTextContent(astHelper.getFirstChild(ast, 'quantifier'));
63✔
1023
        const predicateExpr = astHelper.followPath(ast, ['predicateExpr', '*']);
63✔
1024
        const quantifierInClauses = astHelper
63✔
1025
                .getChildren(ast, 'quantifiedExprInClause')
1026
                .map((inClause) => {
1027
                        const name = astHelper.getQName(
93✔
1028
                                astHelper.followPath(inClause, ['typedVariableBinding', 'varName']),
1029
                        );
1030
                        const sourceExpr = astHelper.followPath(inClause, ['sourceExpr', '*']);
93✔
1031

1032
                        return { name, sourceExpr: compile(sourceExpr, disallowUpdating(compilationOptions)) };
93✔
1033
                });
1034

1035
        return new QuantifiedExpression(
63✔
1036
                quantifier,
1037
                quantifierInClauses,
1038
                compile(predicateExpr, disallowUpdating(compilationOptions)),
1039
                type,
1040
        );
1041
}
1042

1043
function sequence(ast: IAST, compilationOptions: CompilationOptions) {
1044
        const childExpressions = astHelper
3,720✔
1045
                .getChildren(ast, '*')
1046
                .map((arg) => compile(arg, compilationOptions));
5,763✔
1047
        if (childExpressions.length === 1) {
3,720✔
1048
                return childExpressions[0];
336✔
1049
        }
1050

1051
        const type = astHelper.getAttribute(ast, 'type');
3,384✔
1052
        return new SequenceOperator(
3,384✔
1053
                astHelper.getChildren(ast, '*').map((arg) => compile(arg, compilationOptions)),
5,427✔
1054
                type,
1055
        );
1056
}
1057

1058
function simpleMap(ast: IAST, compilationOptions: CompilationOptions) {
1059
        const type = astHelper.getAttribute(ast, 'type');
144✔
1060
        return astHelper.getChildren(ast, '*').reduce((lhs: Expression, rhs: IAST) => {
144✔
1061
                if (lhs === null) {
303✔
1062
                        return compile(rhs, disallowUpdating(compilationOptions));
144✔
1063
                }
1064
                return new SimpleMapOperator(lhs, compile(rhs, disallowUpdating(compilationOptions)), type);
159✔
1065
        }, null);
1066
}
1067

1068
function stringConcatenateOp(ast: IAST, compilationOptions: CompilationOptions) {
1069
        const type = astHelper.getAttribute(ast, 'type');
141✔
1070
        const args = [
141✔
1071
                astHelper.followPath(ast, ['firstOperand', '*']),
1072
                astHelper.followPath(ast, ['secondOperand', '*']),
1073
        ];
1074
        return new FunctionCall(
141✔
1075
                new NamedFunctionRef(
1076
                        {
1077
                                localName: 'concat',
1078
                                namespaceURI: 'http://www.w3.org/2005/xpath-functions',
1079
                                prefix: '',
1080
                        },
1081
                        args.length,
1082
                        type,
1083
                ),
1084
                args.map((arg) => compile(arg, disallowUpdating(compilationOptions))),
282✔
1085
                type,
1086
        );
1087
}
1088

1089
function rangeSequenceExpr(ast: IAST, compilationOptions: CompilationOptions) {
1090
        const type = astHelper.getAttribute(ast, 'type');
102✔
1091
        const args = [
102✔
1092
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'startExpr'), '*') as IAST,
1093
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'endExpr'), '*') as IAST,
1094
        ];
1095

1096
        const ref = new NamedFunctionRef(
102✔
1097
                {
1098
                        localName: 'to',
1099
                        namespaceURI: 'http://fontoxpath/operators',
1100
                        prefix: '',
1101
                },
1102
                args.length,
1103
                type,
1104
        );
1105

1106
        return new FunctionCall(
102✔
1107
                ref,
1108
                args.map((arg) => compile(arg, disallowUpdating(compilationOptions))),
204✔
1109
                type,
1110
        );
1111
}
1112

1113
function typeTest(ast: IAST, _compilationOptions: CompilationOptions) {
1114
        const type = astHelper.getQName(ast);
789✔
1115
        return new TypeTest(type);
789✔
1116
}
1117

1118
function anyMapTest(_ast: IAST, _compilationOptions: CompilationOptions) {
1119
        return new TypeTest({ prefix: '', namespaceURI: null, localName: 'map(*)' });
3✔
1120
}
1121

1122
function anyArrayTest(_ast: IAST, _compilationOptions: CompilationOptions) {
1123
        return new TypeTest({ prefix: '', namespaceURI: null, localName: 'array(*)' });
×
1124
}
1125

1126
function unaryPlus(
1127
        ast: IAST,
1128
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
1129
) {
1130
        const operand = astHelper.getFirstChild(astHelper.getFirstChild(ast, 'operand'), '*');
45✔
1131
        const type = astHelper.getAttribute(ast, 'type');
45✔
1132
        return new Unary('+', compile(operand, compilationOptions), type);
45✔
1133
}
1134

1135
function unaryMinus(
1136
        ast: IAST,
1137
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
1138
) {
1139
        const operand = astHelper.getFirstChild(astHelper.getFirstChild(ast, 'operand'), '*');
120✔
1140
        const type = astHelper.getAttribute(ast, 'type');
120✔
1141
        return new Unary('-', compile(operand, compilationOptions), type);
120✔
1142
}
1143

1144
function unionOp(ast: IAST, compilationOptions: CompilationOptions) {
1145
        const type = astHelper.getAttribute(ast, 'type');
210✔
1146
        return new Union(
210✔
1147
                [
1148
                        compile(
1149
                                astHelper.followPath(ast, ['firstOperand', '*']),
1150
                                disallowUpdating(compilationOptions),
1151
                        ),
1152
                        compile(
1153
                                astHelper.followPath(ast, ['secondOperand', '*']),
1154
                                disallowUpdating(compilationOptions),
1155
                        ),
1156
                ],
1157
                type,
1158
        );
1159
}
1160

1161
function intersectExcept(ast: IAST, compilationOptions: CompilationOptions) {
1162
        const type = astHelper.getAttribute(ast, 'type');
78✔
1163
        return new IntersectExcept(
78✔
1164
                ast[0],
1165
                compile(
1166
                        astHelper.followPath(ast, ['firstOperand', '*']),
1167
                        disallowUpdating(compilationOptions),
1168
                ),
1169
                compile(
1170
                        astHelper.followPath(ast, ['secondOperand', '*']),
1171
                        disallowUpdating(compilationOptions),
1172
                ),
1173
                type,
1174
        );
1175
}
1176

1177
function varRef(ast: IAST, _compilationOptions: CompilationOptions) {
1178
        const { prefix, namespaceURI, localName } = astHelper.getQName(
15,900✔
1179
                astHelper.getFirstChild(ast, 'name'),
1180
        );
1181
        return new VarRef(prefix, namespaceURI, localName);
15,900✔
1182
}
1183

1184
function wildcard(ast: IAST, _compilationOptions: CompilationOptions) {
1185
        if (!astHelper.getFirstChild(ast, 'star')) {
664✔
1186
                return new NameTest({
628✔
1187
                        localName: '*',
1188
                        namespaceURI: null,
1189
                        prefix: '*',
1190
                });
1191
        }
1192
        const uri = astHelper.getFirstChild(ast, 'uri');
36✔
1193
        if (uri) {
36✔
1194
                return new NameTest({
6✔
1195
                        localName: '*',
1196
                        namespaceURI: astHelper.getTextContent(uri),
1197
                        prefix: '',
1198
                });
1199
        }
1200

1201
        // Either the prefix or the localName are 'starred', find out which one
1202
        const ncName = astHelper.getFirstChild(ast, 'NCName');
30✔
1203
        if (astHelper.getFirstChild(ast, '*')[0] === 'star') {
30✔
1204
                // The prefix is 'starred'
1205
                return new NameTest({
24✔
1206
                        localName: astHelper.getTextContent(ncName),
1207
                        namespaceURI: null,
1208
                        prefix: '*',
1209
                });
1210
        }
1211

1212
        // The localName is 'starred'
1213
        return new NameTest({
6✔
1214
                localName: '*',
1215
                namespaceURI: null,
1216
                prefix: astHelper.getTextContent(ncName),
1217
        });
1218
}
1219

1220
// XQuery Node constructors
1221
function dirElementConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1222
        if (!compilationOptions.allowXQuery) {
3,531!
1223
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1224
        }
1225
        const name = astHelper.getQName(astHelper.getFirstChild(ast, 'tagName'));
3,531✔
1226

1227
        const attList = astHelper.getFirstChild(ast, 'attributeList');
3,531✔
1228
        const attributes = attList
3,531✔
1229
                ? astHelper
1230
                                .getChildren(attList, 'attributeConstructor')
1231
                                .map((attr) => compile(attr, disallowUpdating(compilationOptions)))
939✔
1232
                : [];
1233

1234
        const namespaceDecls = attList
3,531✔
1235
                ? astHelper.getChildren(attList, 'namespaceDeclaration').map((namespaceDecl) => {
1236
                                const prefixElement = astHelper.getFirstChild(namespaceDecl, 'prefix');
84✔
1237
                                return {
84✔
1238
                                        prefix: prefixElement ? astHelper.getTextContent(prefixElement) : '',
84✔
1239
                                        uri: astHelper.getTextContent(astHelper.getFirstChild(namespaceDecl, 'uri')),
1240
                                };
1241
                  })
1242
                : [];
1243

1244
        const content = astHelper.getFirstChild(ast, 'elementContent');
3,531✔
1245
        const contentExpressions = content
3,531✔
1246
                ? astHelper
1247
                                .getChildren(content, '*')
1248
                                .map((child) => compile(child, disallowUpdating(compilationOptions)))
2,865✔
1249
                : [];
1250

1251
        return new ElementConstructor(
3,531✔
1252
                name,
1253
                attributes as AttributeConstructor[],
1254
                namespaceDecls,
1255
                contentExpressions,
1256
        );
1257
}
1258

1259
function CDataSection(ast: IAST, _compilationOptions: CompilationOptions) {
1260
        // Walks like a stringliteral, talks like a stringliteral, it's a stringliteral
1261
        return new Literal(astHelper.getTextContent(ast), {
9✔
1262
                type: ValueType.XSSTRING,
1263
                mult: SequenceMultiplicity.EXACTLY_ONE,
1264
        });
1265
}
1266

1267
function attributeConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1268
        if (!compilationOptions.allowXQuery) {
939!
1269
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1270
        }
1271
        const attrName = astHelper.getQName(astHelper.getFirstChild(ast, 'attributeName'));
939✔
1272
        const attrValueElement = astHelper.getFirstChild(ast, 'attributeValue');
939✔
1273
        const attrValue = attrValueElement ? astHelper.getTextContent(attrValueElement) : null;
939✔
1274
        const attrValueExprElement = astHelper.getFirstChild(ast, 'attributeValueExpr');
939✔
1275
        const attrValueExpressions = attrValueExprElement
939✔
1276
                ? astHelper
1277
                                .getChildren(attrValueExprElement, '*')
1278
                                .map((expr) => compile(expr, disallowUpdating(compilationOptions)))
723✔
1279
                : null;
1280
        return new AttributeConstructor(attrName, {
939✔
1281
                value: attrValue,
1282
                valueExprParts: attrValueExpressions,
1283
        });
1284
}
1285

1286
function computedAttributeConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1287
        const tagName = astHelper.getFirstChild(ast, 'tagName');
1,350✔
1288
        let name: { expr: Expression } | { localName: string; namespaceURI: string; prefix: string };
1289
        if (tagName) {
1,350✔
1290
                name = astHelper.getQName(tagName);
687✔
1291
        } else {
1292
                const tagNameExpr = astHelper.getFirstChild(ast, 'tagNameExpr');
663✔
1293
                name = {
663✔
1294
                        expr: compile(
1295
                                astHelper.getFirstChild(tagNameExpr, '*'),
1296
                                disallowUpdating(compilationOptions),
1297
                        ),
1298
                };
1299
        }
1300

1301
        const valueExpr = compile(
1,350✔
1302
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'valueExpr'), '*'),
1303
                disallowUpdating(compilationOptions),
1304
        );
1305

1306
        return new AttributeConstructor(name, {
1,350✔
1307
                valueExprParts: [valueExpr],
1308
        });
1309
}
1310

1311
function computedCommentConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1312
        if (!compilationOptions.allowXQuery) {
453!
1313
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1314
        }
1315
        const argExpr = astHelper.getFirstChild(ast, 'argExpr');
453✔
1316
        const expr = argExpr
453!
1317
                ? compile(astHelper.getFirstChild(argExpr, '*'), disallowUpdating(compilationOptions))
1318
                : null;
1319
        return new CommentConstructor(expr);
453✔
1320
}
1321

1322
function computedTextConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1323
        if (!compilationOptions.allowXQuery) {
429!
1324
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1325
        }
1326
        const argExpr = astHelper.getFirstChild(ast, 'argExpr');
429✔
1327
        const expr = argExpr
429!
1328
                ? compile(astHelper.getFirstChild(argExpr, '*'), disallowUpdating(compilationOptions))
1329
                : null;
1330
        return new TextConstructor(expr);
429✔
1331
}
1332

1333
function computedElementConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1334
        const tagName = astHelper.getFirstChild(ast, 'tagName');
×
1335
        let name: { expr: Expression } | { localName: string; namespaceURI: string; prefix: string };
1336
        if (tagName) {
×
1337
                name = astHelper.getQName(tagName);
×
1338
        } else {
1339
                const tagNameExpr = astHelper.getFirstChild(ast, 'tagNameExpr');
×
1340
                name = {
×
1341
                        expr: compile(
1342
                                astHelper.getFirstChild(tagNameExpr, '*'),
1343
                                disallowUpdating(compilationOptions),
1344
                        ),
1345
                };
1346
        }
1347

1348
        const content = astHelper.getFirstChild(ast, 'contentExpr');
×
1349
        const contentExpressions = content
×
1350
                ? astHelper
1351
                                .getChildren(content, '*')
1352
                                .map((child) => compile(child, disallowUpdating(compilationOptions)))
×
1353
                : [];
1354

1355
        return new ElementConstructor(name, [], [], contentExpressions);
×
1356
}
1357

1358
function computedPIConstructor(ast: IAST, compilationOptions: CompilationOptions) {
1359
        if (!compilationOptions.allowXQuery) {
279!
1360
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1361
        }
1362

1363
        const targetExpr = astHelper.getFirstChild(ast, 'piTargetExpr');
279✔
1364
        const target = astHelper.getFirstChild(ast, 'piTarget');
279✔
1365
        const piValueExpr = astHelper.getFirstChild(ast, 'piValueExpr');
279✔
1366
        const type = astHelper.getAttribute(ast, 'type');
279✔
1367

1368
        return new PIConstructor(
279✔
1369
                {
1370
                        targetExpr: targetExpr
279✔
1371
                                ? compile(
1372
                                                astHelper.getFirstChild(targetExpr, '*'),
1373
                                                disallowUpdating(compilationOptions),
1374
                                  )
1375
                                : null,
1376
                        targetValue: target ? astHelper.getTextContent(target) : null,
279✔
1377
                },
1378
                piValueExpr
279!
1379
                        ? compile(
1380
                                        astHelper.getFirstChild(piValueExpr, '*'),
1381
                                        disallowUpdating(compilationOptions),
1382
                          )
1383
                        : new SequenceOperator([], type),
1384
        );
1385
}
1386

1387
function deleteExpression(
1388
        ast: IAST,
1389
        compilationOptions: { allowUpdating?: boolean; allowXQuery?: boolean },
1390
) {
1391
        const targetExpr = compile(astHelper.followPath(ast, ['targetExpr', '*']), compilationOptions);
1,284✔
1392
        return new DeleteExpression(targetExpr);
1,284✔
1393
}
1394

1395
function insertExpression(ast: IAST, compilationOptions: CompilationOptions) {
1396
        const sourceExpr = compile(astHelper.followPath(ast, ['sourceExpr', '*']), compilationOptions);
1,890✔
1397
        let targetChoice: TargetChoice;
1398
        const insertTargetChoice = astHelper.getChildren(ast, '*')[1];
1,890✔
1399
        switch (insertTargetChoice[0]) {
1,890✔
1400
                case 'insertAfter':
1401
                        targetChoice = TargetChoice.INSERT_AFTER;
363✔
1402
                        break;
363✔
1403
                case 'insertBefore':
1404
                        targetChoice = TargetChoice.INSERT_BEFORE;
381✔
1405
                        break;
381✔
1406
                case 'insertInto': {
1407
                        const insertAfterChoice = astHelper.getFirstChild(insertTargetChoice, '*');
1,146✔
1408
                        if (insertAfterChoice) {
1,146✔
1409
                                targetChoice =
396✔
1410
                                        insertAfterChoice[0] === 'insertAsFirst'
396✔
1411
                                                ? TargetChoice.INSERT_INTO_AS_FIRST
1412
                                                : TargetChoice.INSERT_INTO_AS_LAST;
1413
                        } else {
1414
                                targetChoice = TargetChoice.INSERT_INTO;
750✔
1415
                        }
1416
                        break;
1,146✔
1417
                }
1418
        }
1419
        const targetExpr = compile(astHelper.followPath(ast, ['targetExpr', '*']), compilationOptions);
1,890✔
1420
        return new InsertExpression(sourceExpr, targetChoice, targetExpr);
1,890✔
1421
}
1422

1423
function renameExpression(ast: IAST, compilationOptions: CompilationOptions) {
1424
        const targetExpr = compile(astHelper.followPath(ast, ['targetExpr', '*']), compilationOptions);
843✔
1425
        const newNameExpr = compile(
843✔
1426
                astHelper.followPath(ast, ['newNameExpr', '*']),
1427
                compilationOptions,
1428
        );
1429
        return new RenameExpression(targetExpr, newNameExpr);
843✔
1430
}
1431

1432
function replaceExpression(ast: IAST, compilationOptions: CompilationOptions) {
1433
        const isReplaceValue = !!astHelper.getFirstChild(ast, 'replaceValue');
2,052✔
1434
        const targetExpr = compile(astHelper.followPath(ast, ['targetExpr', '*']), compilationOptions);
2,052✔
1435
        const replacementExpr = compile(
2,052✔
1436
                astHelper.followPath(ast, ['replacementExpr', '*']),
1437
                compilationOptions,
1438
        );
1439
        return new ReplaceExpression(isReplaceValue, targetExpr, replacementExpr);
2,052✔
1440
}
1441

1442
function transformExpression(ast: IAST, compilationOptions: CompilationOptions) {
1443
        const transformCopies = astHelper
606✔
1444
                .getChildren(astHelper.getFirstChild(ast, 'transformCopies'), 'transformCopy')
1445
                .map((transformCopy) => {
1446
                        const varName = astHelper.getQName(
633✔
1447
                                astHelper.getFirstChild(astHelper.getFirstChild(transformCopy, 'varRef'), 'name'),
1448
                        );
1449
                        const sourceExpr = compile(
633✔
1450
                                astHelper.getFirstChild(astHelper.getFirstChild(transformCopy, 'copySource'), '*'),
1451
                                compilationOptions,
1452
                        );
1453
                        return {
633✔
1454
                                sourceExpr,
1455
                                varRef: new QName(varName.prefix, varName.namespaceURI, varName.localName),
1456
                        };
1457
                });
1458
        const modifyExpr = compile(
606✔
1459
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'modifyExpr'), '*'),
1460
                compilationOptions,
1461
        );
1462
        const returnExpr = compile(
606✔
1463
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'returnExpr'), '*'),
1464
                compilationOptions,
1465
        );
1466
        return new TransformExpression(transformCopies, modifyExpr, returnExpr);
606✔
1467
}
1468

1469
function typeswitchExpr(ast: IAST, compilationOptions: CompilationOptions) {
1470
        if (!compilationOptions.allowXQuery) {
186!
1471
                throw new Error('XPST0003: Use of XQuery functionality is not allowed in XPath context');
×
1472
        }
1473

1474
        const type = astHelper.getAttribute(ast, 'type');
186✔
1475

1476
        const argExpr = compile(
186✔
1477
                astHelper.getFirstChild(astHelper.getFirstChild(ast, 'argExpr'), '*'),
1478
                compilationOptions,
1479
        );
1480

1481
        const caseClause = astHelper.getChildren(ast, 'typeswitchExprCaseClause');
186✔
1482

1483
        const caseClauseExpressions = caseClause.map((caseClauseExpression) => {
186✔
1484
                const sequenceTypesAstNodes: IAST[] =
1485
                        astHelper.getChildren(caseClauseExpression, 'sequenceTypeUnion').length === 0
384✔
1486
                                ? [astHelper.getFirstChild(caseClauseExpression, 'sequenceType')]
1487
                                : astHelper.getChildren(
1488
                                                astHelper.getFirstChild(caseClauseExpression, 'sequenceTypeUnion'),
1489
                                                'sequenceType',
1490
                                  );
1491

1492
                const resultExpression = compile(
384✔
1493
                        astHelper.followPath(caseClauseExpression, ['resultExpr', '*']),
1494
                        compilationOptions,
1495
                ) as PossiblyUpdatingExpression;
1496

1497
                return {
384✔
1498
                        caseClauseExpression: resultExpression,
1499
                        typeTests: sequenceTypesAstNodes.map((sequenceTypeAstNode: IAST) => {
1500
                                const occurrenceIndicator = astHelper.getFirstChild(
387✔
1501
                                        sequenceTypeAstNode,
1502
                                        'occurrenceIndicator',
1503
                                );
1504
                                return {
387✔
1505
                                        occurrenceIndicator: (occurrenceIndicator
387✔
1506
                                                ? astHelper.getTextContent(occurrenceIndicator)
1507
                                                : '') as '*' | '?' | '+' | '',
1508
                                        typeTest: compile(
1509
                                                astHelper.getFirstChild(sequenceTypeAstNode, '*'),
1510
                                                compilationOptions,
1511
                                        ),
1512
                                };
1513
                        }),
1514
                };
1515
        });
1516

1517
        const defaultExpression = compile(
186✔
1518
                astHelper.followPath(ast, ['typeswitchExprDefaultClause', 'resultExpr', '*']),
1519
                compilationOptions,
1520
        ) as PossiblyUpdatingExpression;
1521

1522
        return new TypeSwitchExpression(argExpr, caseClauseExpressions, defaultExpression, type);
186✔
1523
}
1524

1525
export default function (xPathAst: IAST, compilationOptions: CompilationOptions): Expression {
15✔
1526
        return compile(xPathAst, compilationOptions);
12,426✔
1527
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc