@tradik/xslt3 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +28 -0
- package/README.md +98 -2
- package/package.json +43 -4
- package/src/errors.js +20 -0
- package/src/functions/aggregates.js +144 -0
- package/src/functions/arraySort.js +33 -0
- package/src/functions/arrays.js +181 -0
- package/src/functions/attributeTypes.js +58 -0
- package/src/functions/baseUris.js +82 -0
- package/src/functions/collations.js +127 -0
- package/src/functions/collections.js +84 -0
- package/src/functions/context.js +83 -0
- package/src/functions/core.js +167 -0
- package/src/functions/datetime.js +203 -0
- package/src/functions/deepEqual.js +132 -0
- package/src/functions/diagnostics.js +88 -0
- package/src/functions/focus.js +34 -0
- package/src/functions/format/dateComponents.js +176 -0
- package/src/functions/format/datePicture.js +115 -0
- package/src/functions/format/decimalFormat.js +65 -0
- package/src/functions/format/digitPattern.js +145 -0
- package/src/functions/format/formatDateTime.js +204 -0
- package/src/functions/format/formatInteger.js +109 -0
- package/src/functions/format/formatNumber.js +165 -0
- package/src/functions/format/fractionFormat.js +80 -0
- package/src/functions/format/germanWords.js +89 -0
- package/src/functions/format/numberLanguages.js +41 -0
- package/src/functions/format/numberPicture.js +202 -0
- package/src/functions/format/numberWords.js +155 -0
- package/src/functions/format/place.js +74 -0
- package/src/functions/format/timezoneFormat.js +89 -0
- package/src/functions/higherOrder.js +106 -0
- package/src/functions/ids.js +127 -0
- package/src/functions/json/index.js +17 -0
- package/src/functions/json/jsonChars.js +110 -0
- package/src/functions/json/jsonParser.js +203 -0
- package/src/functions/json/jsonStrings.js +116 -0
- package/src/functions/json/jsonToXml.js +116 -0
- package/src/functions/json/parseJson.js +119 -0
- package/src/functions/json/xmlRepresentation.js +103 -0
- package/src/functions/json/xmlToJson.js +147 -0
- package/src/functions/library.js +47 -0
- package/src/functions/mapArrayEqual.js +41 -0
- package/src/functions/maps.js +152 -0
- package/src/functions/math.js +73 -0
- package/src/functions/nodePaths.js +181 -0
- package/src/functions/nodes.js +199 -0
- package/src/functions/numeric.js +148 -0
- package/src/functions/options.js +58 -0
- package/src/functions/parseIetfDate.js +104 -0
- package/src/functions/parseXml.js +77 -0
- package/src/functions/qnames.js +68 -0
- package/src/functions/random.js +128 -0
- package/src/functions/regex/analyzeString.js +144 -0
- package/src/functions/regex/charClass.js +112 -0
- package/src/functions/regex/compile.js +81 -0
- package/src/functions/regex/escapes.js +109 -0
- package/src/functions/regex/parser.js +169 -0
- package/src/functions/regex/reader.js +37 -0
- package/src/functions/regex/regexFunctions.js +140 -0
- package/src/functions/regex/translate.js +102 -0
- package/src/functions/regex/unicodeBlocks.js +194 -0
- package/src/functions/registry.js +140 -0
- package/src/functions/sequences.js +173 -0
- package/src/functions/serializeFunction.js +53 -0
- package/src/functions/signatures.js +105 -0
- package/src/functions/sort.js +102 -0
- package/src/functions/stringMatch.js +45 -0
- package/src/functions/strings.js +194 -0
- package/src/functions/stringsCore.js +66 -0
- package/src/functions/support.js +106 -0
- package/src/functions/textDecoding.js +117 -0
- package/src/functions/ucaCollation.js +127 -0
- package/src/functions/unparsedText.js +71 -0
- package/src/functions/uri.js +166 -0
- package/src/functions/xqueryModule.js +24 -0
- package/src/index.js +38 -0
- package/src/items/array.js +84 -0
- package/src/items/function.js +45 -0
- package/src/items/map.js +120 -0
- package/src/items/mapKey.js +82 -0
- package/src/serialize/index.js +129 -0
- package/src/serialize/markup/content.js +74 -0
- package/src/serialize/markup/dialect.js +178 -0
- package/src/serialize/markup/htmlData.js +70 -0
- package/src/serialize/markup/namespaces.js +145 -0
- package/src/serialize/markup/prolog.js +91 -0
- package/src/serialize/markup/writer.js +183 -0
- package/src/serialize/methods/adaptive.js +131 -0
- package/src/serialize/methods/json.js +158 -0
- package/src/serialize/methods/text.js +43 -0
- package/src/serialize/output/buffer.js +48 -0
- package/src/serialize/output/encoding.js +146 -0
- package/src/serialize/output/expander.js +204 -0
- package/src/serialize/params/fromElement.js +141 -0
- package/src/serialize/params/fromMap.js +128 -0
- package/src/serialize/params/names.js +69 -0
- package/src/serialize/params/settings.js +153 -0
- package/src/serialize/params/values.js +163 -0
- package/src/serialize/sequence.js +111 -0
- package/src/xdm/arithmetic.js +155 -0
- package/src/xdm/atomic.js +142 -0
- package/src/xdm/binary.js +92 -0
- package/src/xdm/calendar.js +71 -0
- package/src/xdm/cast.js +176 -0
- package/src/xdm/compare.js +192 -0
- package/src/xdm/datetime.js +206 -0
- package/src/xdm/decimal.js +200 -0
- package/src/xdm/duration.js +128 -0
- package/src/xdm/generalCompare.js +152 -0
- package/src/xdm/index.js +47 -0
- package/src/xdm/lexical.js +126 -0
- package/src/xdm/nodes.js +170 -0
- package/src/xdm/numeric.js +122 -0
- package/src/xdm/qname.js +65 -0
- package/src/xdm/strings.js +52 -0
- package/src/xdm/temporalArithmetic.js +180 -0
- package/src/xdm/timeline.js +68 -0
- package/src/xdm/types.js +178 -0
- package/src/xpath/eval/atomics.js +45 -0
- package/src/xpath/eval/axes.js +212 -0
- package/src/xpath/eval/calls.js +168 -0
- package/src/xpath/eval/coercion.js +146 -0
- package/src/xpath/eval/compiler.js +71 -0
- package/src/xpath/eval/constructors.js +129 -0
- package/src/xpath/eval/decimalFormats.js +93 -0
- package/src/xpath/eval/descendantMemo.js +77 -0
- package/src/xpath/eval/documentOrder.js +142 -0
- package/src/xpath/eval/domNodes.js +174 -0
- package/src/xpath/eval/dynamicCall.js +27 -0
- package/src/xpath/eval/dynamicContext.js +161 -0
- package/src/xpath/eval/flow.js +115 -0
- package/src/xpath/eval/functionItems.js +194 -0
- package/src/xpath/eval/groupedChildren.js +88 -0
- package/src/xpath/eval/inline.js +71 -0
- package/src/xpath/eval/namespaceNodes.js +90 -0
- package/src/xpath/eval/nodeTests.js +164 -0
- package/src/xpath/eval/operators.js +190 -0
- package/src/xpath/eval/paths.js +163 -0
- package/src/xpath/eval/positional.js +81 -0
- package/src/xpath/eval/predicates.js +56 -0
- package/src/xpath/eval/primary.js +99 -0
- package/src/xpath/eval/resources.js +149 -0
- package/src/xpath/eval/scope.js +108 -0
- package/src/xpath/eval/sequenceType.js +193 -0
- package/src/xpath/eval/staticContext.js +142 -0
- package/src/xpath/eval/steps.js +98 -0
- package/src/xpath/eval/subtype.js +154 -0
- package/src/xpath/eval/typeExprs.js +147 -0
- package/src/xpath/eval/typeNames.js +101 -0
- package/src/xpath/eval/uris.js +21 -0
- package/src/xpath/eval/values.js +64 -0
- package/src/xpath/index.js +217 -0
- package/src/xpath/syntax/ast.js +130 -0
- package/src/xpath/syntax/chars.js +58 -0
- package/src/xpath/syntax/constructors.js +144 -0
- package/src/xpath/syntax/expressions.js +107 -0
- package/src/xpath/syntax/index.js +44 -0
- package/src/xpath/syntax/kindTests.js +134 -0
- package/src/xpath/syntax/lexer.js +220 -0
- package/src/xpath/syntax/operators.js +111 -0
- package/src/xpath/syntax/paths.js +102 -0
- package/src/xpath/syntax/postfix.js +91 -0
- package/src/xpath/syntax/primaries.js +149 -0
- package/src/xpath/syntax/steps.js +146 -0
- package/src/xpath/syntax/syntaxError.js +31 -0
- package/src/xpath/syntax/tokenStream.js +163 -0
- package/src/xpath/syntax/typeAst.js +76 -0
- package/src/xpath/syntax/typeOperators.js +127 -0
- package/src/xpath/syntax/types.js +158 -0
- package/src/xslt/api.js +153 -0
- package/src/xslt/compiler/acceptance.js +178 -0
- package/src/xslt/compiler/accumulatorDecl.js +146 -0
- package/src/xslt/compiler/attributeValues.js +174 -0
- package/src/xslt/compiler/attributes.js +211 -0
- package/src/xslt/compiler/avt.js +103 -0
- package/src/xslt/compiler/body.js +154 -0
- package/src/xslt/compiler/children.js +105 -0
- package/src/xslt/compiler/compatibility.js +130 -0
- package/src/xslt/compiler/components.js +179 -0
- package/src/xslt/compiler/contextItem.js +95 -0
- package/src/xslt/compiler/decimalFormats.js +132 -0
- package/src/xslt/compiler/declarationKinds.js +82 -0
- package/src/xslt/compiler/elementInfo.js +246 -0
- package/src/xslt/compiler/expressions.js +175 -0
- package/src/xslt/compiler/globalContextItem.js +92 -0
- package/src/xslt/compiler/globals.js +209 -0
- package/src/xslt/compiler/linker.js +134 -0
- package/src/xslt/compiler/miscDecl.js +171 -0
- package/src/xslt/compiler/modeDecl.js +88 -0
- package/src/xslt/compiler/moduleLoading.js +111 -0
- package/src/xslt/compiler/modules.js +158 -0
- package/src/xslt/compiler/outputDecl.js +209 -0
- package/src/xslt/compiler/overrideLinks.js +141 -0
- package/src/xslt/compiler/packageChecks.js +84 -0
- package/src/xslt/compiler/packageRegistry.js +95 -0
- package/src/xslt/compiler/packageTable.js +181 -0
- package/src/xslt/compiler/packageVersions.js +143 -0
- package/src/xslt/compiler/packages.js +138 -0
- package/src/xslt/compiler/registry.js +234 -0
- package/src/xslt/compiler/staticStage.js +197 -0
- package/src/xslt/compiler/staticVariables.js +93 -0
- package/src/xslt/compiler/stylesheet.js +205 -0
- package/src/xslt/compiler/tailPosition.js +74 -0
- package/src/xslt/compiler/templatesDecl.js +180 -0
- package/src/xslt/compiler/useAccumulators.js +76 -0
- package/src/xslt/compiler/useWhen.js +94 -0
- package/src/xslt/compiler/visibility.js +160 -0
- package/src/xslt/index.js +11 -0
- package/src/xslt/instructions/analyzeString.js +137 -0
- package/src/xslt/instructions/assert.js +72 -0
- package/src/xslt/instructions/attributeSets.js +115 -0
- package/src/xslt/instructions/conditional.js +99 -0
- package/src/xslt/instructions/control.js +123 -0
- package/src/xslt/instructions/copy.js +151 -0
- package/src/xslt/instructions/elements.js +161 -0
- package/src/xslt/instructions/evaluate.js +189 -0
- package/src/xslt/instructions/fork.js +35 -0
- package/src/xslt/instructions/grouping.js +106 -0
- package/src/xslt/instructions/index.js +94 -0
- package/src/xslt/instructions/iterate.js +182 -0
- package/src/xslt/instructions/literal.js +142 -0
- package/src/xslt/instructions/maps.js +72 -0
- package/src/xslt/instructions/merge.js +190 -0
- package/src/xslt/instructions/number.js +205 -0
- package/src/xslt/instructions/output.js +102 -0
- package/src/xslt/instructions/params.js +110 -0
- package/src/xslt/instructions/resultDocument.js +143 -0
- package/src/xslt/instructions/sort.js +114 -0
- package/src/xslt/instructions/sourceDocument.js +73 -0
- package/src/xslt/instructions/templates.js +153 -0
- package/src/xslt/instructions/text.js +151 -0
- package/src/xslt/instructions/try.js +186 -0
- package/src/xslt/instructions/variables.js +119 -0
- package/src/xslt/names.js +204 -0
- package/src/xslt/patterns/axisOrigins.js +121 -0
- package/src/xslt/patterns/compile.js +151 -0
- package/src/xslt/patterns/general.js +132 -0
- package/src/xslt/patterns/grammar.js +190 -0
- package/src/xslt/patterns/modes.js +153 -0
- package/src/xslt/patterns/paths.js +130 -0
- package/src/xslt/patterns/patternChecks.js +29 -0
- package/src/xslt/patterns/steps.js +162 -0
- package/src/xslt/processor.js +178 -0
- package/src/xslt/runtime/accumulatorFunctions.js +48 -0
- package/src/xslt/runtime/accumulators.js +174 -0
- package/src/xslt/runtime/apply.js +153 -0
- package/src/xslt/runtime/atomicKeys.js +36 -0
- package/src/xslt/runtime/availability.js +162 -0
- package/src/xslt/runtime/baseUri.js +64 -0
- package/src/xslt/runtime/compilerHook.js +27 -0
- package/src/xslt/runtime/conditional.js +110 -0
- package/src/xslt/runtime/context.js +106 -0
- package/src/xslt/runtime/copy.js +123 -0
- package/src/xslt/runtime/copyFunctions.js +71 -0
- package/src/xslt/runtime/copyOrigins.js +60 -0
- package/src/xslt/runtime/defaultMethod.js +42 -0
- package/src/xslt/runtime/documentContent.js +93 -0
- package/src/xslt/runtime/dynamicXPath.js +90 -0
- package/src/xslt/runtime/functionCache.js +65 -0
- package/src/xslt/runtime/functionCall.js +57 -0
- package/src/xslt/runtime/functions.js +191 -0
- package/src/xslt/runtime/groups.js +108 -0
- package/src/xslt/runtime/invocation.js +156 -0
- package/src/xslt/runtime/iteration.js +107 -0
- package/src/xslt/runtime/keys.js +141 -0
- package/src/xslt/runtime/lazy.js +97 -0
- package/src/xslt/runtime/locations.js +86 -0
- package/src/xslt/runtime/machine.js +137 -0
- package/src/xslt/runtime/merging.js +167 -0
- package/src/xslt/runtime/namespaceScope.js +141 -0
- package/src/xslt/runtime/numberFormat.js +89 -0
- package/src/xslt/runtime/numbering.js +168 -0
- package/src/xslt/runtime/params.js +59 -0
- package/src/xslt/runtime/properties.js +103 -0
- package/src/xslt/runtime/rawResults.js +82 -0
- package/src/xslt/runtime/sequenceReceiver.js +102 -0
- package/src/xslt/runtime/snapshot.js +66 -0
- package/src/xslt/runtime/sortKeys.js +119 -0
- package/src/xslt/runtime/streamAvailable.js +60 -0
- package/src/xslt/runtime/strip.js +102 -0
- package/src/xslt/runtime/transformFunction.js +169 -0
- package/src/xslt/runtime/transformation.js +168 -0
- package/src/xslt/runtime/transformationContext.js +85 -0
- package/src/xslt/runtime/treeReceiver.js +194 -0
- package/src/xslt/runtime/values.js +92 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compilers of the operator expressions: and/or, comparisons, `||`, `to`,
|
|
3
|
+
* arithmetic and unary plus/minus (XPath 3.1 sections 3.4 to 3.8), with
|
|
4
|
+
* the XPath 1.0 compatibility mode rules for arithmetic.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xpath/eval/operators
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { XPathError } from "../../errors.js";
|
|
10
|
+
import { arithmetic, unaryArithmetic } from "../../xdm/arithmetic.js";
|
|
11
|
+
import { AtomicValue, isNode } from "../../xdm/atomic.js";
|
|
12
|
+
import { cast } from "../../xdm/cast.js";
|
|
13
|
+
import { valueCompare } from "../../xdm/compare.js";
|
|
14
|
+
import { generalCompare, toNumber } from "../../xdm/generalCompare.js";
|
|
15
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
16
|
+
import { atomize, effectiveBooleanValue } from "../../xdm/nodes.js";
|
|
17
|
+
import { derivesFrom, isNumericType, types } from "../../xdm/types.js";
|
|
18
|
+
import { atomizeOptional, booleanItem, stringItem } from "./atomics.js";
|
|
19
|
+
|
|
20
|
+
/** Largest sequence a range expression may build. */
|
|
21
|
+
export const MAX_RANGE = 2 ** 25;
|
|
22
|
+
|
|
23
|
+
const EMPTY = Object.freeze([]);
|
|
24
|
+
const NAN = new AtomicValue(types.double, NaN);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Operand of an arithmetic operator in XPath 1.0 compatibility mode: the
|
|
28
|
+
* first atomized item, as xs:double unless it is a duration or date/time.
|
|
29
|
+
* @param {Array} sequence
|
|
30
|
+
* @returns {AtomicValue}
|
|
31
|
+
*/
|
|
32
|
+
function compatibleOperand(sequence) {
|
|
33
|
+
const first = atomize(sequence.slice(0, 1))[0];
|
|
34
|
+
if (first === undefined) return NAN;
|
|
35
|
+
const primitive = first.type.primitive.localName;
|
|
36
|
+
const toDouble =
|
|
37
|
+
isNumericType(first.type) ||
|
|
38
|
+
["boolean", "string", "untypedAtomic", "anyURI"].includes(primitive);
|
|
39
|
+
return toDouble ? toNumber(first) : first;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {Array} sequence
|
|
44
|
+
* @returns {*} the single node of a node comparison operand, undefined
|
|
45
|
+
* when empty
|
|
46
|
+
* @throws {XPathError} XPTY0004 when the operand is not one node
|
|
47
|
+
*/
|
|
48
|
+
function nodeOperand(sequence) {
|
|
49
|
+
if (sequence.length === 0) return undefined;
|
|
50
|
+
if (sequence.length > 1 || !isNode(sequence[0])) {
|
|
51
|
+
throw new XPathError(
|
|
52
|
+
"XPTY0004",
|
|
53
|
+
"A node comparison operand must be a single node",
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
return sequence[0];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Operand of a value comparison, xs:untypedAtomic cast to xs:string.
|
|
61
|
+
* @param {Array} sequence
|
|
62
|
+
* @returns {AtomicValue|undefined}
|
|
63
|
+
*/
|
|
64
|
+
function valueOperand(sequence) {
|
|
65
|
+
const value = atomizeOptional(sequence, "operand of a value comparison");
|
|
66
|
+
return value?.type === types.untypedAtomic
|
|
67
|
+
? cast(value, types.string)
|
|
68
|
+
: value;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Operand of a range expression.
|
|
73
|
+
* @param {Array} sequence
|
|
74
|
+
* @returns {bigint|undefined}
|
|
75
|
+
*/
|
|
76
|
+
function rangeOperand(sequence) {
|
|
77
|
+
let value = atomizeOptional(sequence, "operand of to");
|
|
78
|
+
if (value === undefined) return undefined;
|
|
79
|
+
if (value.type === types.untypedAtomic) value = cast(value, types.integer);
|
|
80
|
+
if (!derivesFrom(value.type, types.integer)) {
|
|
81
|
+
throw new XPathError("XPTY0004", "The operands of to must be integers");
|
|
82
|
+
}
|
|
83
|
+
return value.value;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Comparison compilers by kind. */
|
|
87
|
+
const comparisons = {
|
|
88
|
+
general: (op, left, right, compatible) => (ctx) => {
|
|
89
|
+
const options = compatible
|
|
90
|
+
? ctx.dyn.compatibleCompareOptions
|
|
91
|
+
: ctx.dyn.compareOptions;
|
|
92
|
+
return [booleanItem(generalCompare(left(ctx), op, right(ctx), options))];
|
|
93
|
+
},
|
|
94
|
+
value: (op, left, right) => (ctx) => {
|
|
95
|
+
const a = valueOperand(left(ctx));
|
|
96
|
+
const b = valueOperand(right(ctx));
|
|
97
|
+
if (a === undefined || b === undefined) return EMPTY;
|
|
98
|
+
return [booleanItem(valueCompare(a, op, b, ctx.dyn.compareOptions))];
|
|
99
|
+
},
|
|
100
|
+
node: (op, left, right) => (ctx) => {
|
|
101
|
+
const a = nodeOperand(left(ctx));
|
|
102
|
+
const b = nodeOperand(right(ctx));
|
|
103
|
+
if (a === undefined || b === undefined) return EMPTY;
|
|
104
|
+
if (op === "is") return [booleanItem(a === b)];
|
|
105
|
+
const order = ctx.dyn.order.compare(a, b);
|
|
106
|
+
return [booleanItem(op === "<<" ? order < 0 : order > 0)];
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/** Compilers by node type. */
|
|
111
|
+
export const operatorCompilers = {
|
|
112
|
+
LogicalExpr(node, scope, compile) {
|
|
113
|
+
const left = compile(node.left, scope);
|
|
114
|
+
const right = compile(node.right, scope);
|
|
115
|
+
const isOr = node.operator === "or";
|
|
116
|
+
return (ctx) => {
|
|
117
|
+
const first = effectiveBooleanValue(left(ctx));
|
|
118
|
+
if (first === isOr) return [booleanItem(isOr)];
|
|
119
|
+
return [booleanItem(effectiveBooleanValue(right(ctx)))];
|
|
120
|
+
};
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
ComparisonExpr(node, scope, compile) {
|
|
124
|
+
return comparisons[node.kind](
|
|
125
|
+
node.operator,
|
|
126
|
+
compile(node.left, scope),
|
|
127
|
+
compile(node.right, scope),
|
|
128
|
+
scope.sc.backwardsCompatible,
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
StringConcatExpr(node, scope, compile) {
|
|
133
|
+
const left = compile(node.left, scope);
|
|
134
|
+
const right = compile(node.right, scope);
|
|
135
|
+
const text = (sequence) => {
|
|
136
|
+
const value = atomizeOptional(sequence, "operand of ||");
|
|
137
|
+
return value === undefined ? "" : canonicalString(value);
|
|
138
|
+
};
|
|
139
|
+
return (ctx) => [stringItem(text(left(ctx)) + text(right(ctx)))];
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
RangeExpr(node, scope, compile) {
|
|
143
|
+
const left = compile(node.left, scope);
|
|
144
|
+
const right = compile(node.right, scope);
|
|
145
|
+
return (ctx) => {
|
|
146
|
+
const low = rangeOperand(left(ctx));
|
|
147
|
+
const high = rangeOperand(right(ctx));
|
|
148
|
+
if (low === undefined || high === undefined || low > high) return EMPTY;
|
|
149
|
+
if (high - low >= BigInt(MAX_RANGE)) {
|
|
150
|
+
throw new XPathError("XPDY0130", "The range is too large");
|
|
151
|
+
}
|
|
152
|
+
const result = [];
|
|
153
|
+
for (let i = low; i <= high; i++) {
|
|
154
|
+
result.push(new AtomicValue(types.integer, i));
|
|
155
|
+
}
|
|
156
|
+
return result;
|
|
157
|
+
};
|
|
158
|
+
},
|
|
159
|
+
|
|
160
|
+
ArithmeticExpr(node, scope, compile) {
|
|
161
|
+
const left = compile(node.left, scope);
|
|
162
|
+
const right = compile(node.right, scope);
|
|
163
|
+
const op = node.operator;
|
|
164
|
+
if (scope.sc.backwardsCompatible) {
|
|
165
|
+
return (ctx) => {
|
|
166
|
+
const a = compatibleOperand(left(ctx));
|
|
167
|
+
const b = compatibleOperand(right(ctx));
|
|
168
|
+
return [arithmetic(a, op, b, ctx.dyn.compareOptions)];
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
return (ctx) => {
|
|
172
|
+
const a = atomizeOptional(left(ctx), `operand of ${op}`);
|
|
173
|
+
const b = atomizeOptional(right(ctx), `operand of ${op}`);
|
|
174
|
+
if (a === undefined || b === undefined) return EMPTY;
|
|
175
|
+
return [arithmetic(a, op, b, ctx.dyn.compareOptions)];
|
|
176
|
+
};
|
|
177
|
+
},
|
|
178
|
+
|
|
179
|
+
UnaryExpr(node, scope, compile) {
|
|
180
|
+
const operand = compile(node.operand, scope);
|
|
181
|
+
const op = node.operator;
|
|
182
|
+
if (scope.sc.backwardsCompatible) {
|
|
183
|
+
return (ctx) => [unaryArithmetic(op, compatibleOperand(operand(ctx)))];
|
|
184
|
+
}
|
|
185
|
+
return (ctx) => {
|
|
186
|
+
const a = atomizeOptional(operand(ctx), `operand of unary ${op}`);
|
|
187
|
+
return a === undefined ? EMPTY : [unaryArithmetic(op, a)];
|
|
188
|
+
};
|
|
189
|
+
},
|
|
190
|
+
};
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compilers of path expressions, axis steps, predicates, filter
|
|
3
|
+
* expressions and the node set operators (XPath 3.1 sections 3.3, 3.4).
|
|
4
|
+
*
|
|
5
|
+
* `//T` is compiled as `descendant::T` when the predicates of T do not
|
|
6
|
+
* depend on the context position (see positional.js), instead of
|
|
7
|
+
* `descendant-or-self::node()/child::T`, which gives the same nodes.
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/xpath/eval/paths
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { XPathError } from "../../errors.js";
|
|
13
|
+
import { isNode } from "../../xdm/atomic.js";
|
|
14
|
+
import { REVERSE_AXES } from "./axes.js";
|
|
15
|
+
import { axisNodes } from "./descendantMemo.js";
|
|
16
|
+
import { rootOf } from "./domNodes.js";
|
|
17
|
+
import {
|
|
18
|
+
compileGroupedChildren,
|
|
19
|
+
isGroupedChildren,
|
|
20
|
+
} from "./groupedChildren.js";
|
|
21
|
+
import { compileNodeTest } from "./nodeTests.js";
|
|
22
|
+
import { isPositionIndependent } from "./positional.js";
|
|
23
|
+
import { compilePredicate } from "./predicates.js";
|
|
24
|
+
import { contextItem } from "./scope.js";
|
|
25
|
+
import { stepper } from "./steps.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {import("./scope.js").Context} ctx
|
|
29
|
+
* @param {string} code - Error code when the context item is not a node
|
|
30
|
+
* @returns {Node} the context node
|
|
31
|
+
*/
|
|
32
|
+
function contextNode(ctx, code) {
|
|
33
|
+
const item = contextItem(ctx);
|
|
34
|
+
if (!isNode(item)) {
|
|
35
|
+
throw new XPathError(code, "The context item is not a node");
|
|
36
|
+
}
|
|
37
|
+
return item;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Whether a step is `descendant-or-self::node()` and the next one a child
|
|
42
|
+
* step whose predicates do not depend on the position, which together are
|
|
43
|
+
* a descendant step.
|
|
44
|
+
*/
|
|
45
|
+
const isDescendantShortcut = (step, next, sc) =>
|
|
46
|
+
step.type === "AxisStep" &&
|
|
47
|
+
step.axis === "descendant-or-self" &&
|
|
48
|
+
step.nodeTest.type === "AnyKindTest" &&
|
|
49
|
+
step.predicates.length === 0 &&
|
|
50
|
+
next?.type === "AxisStep" &&
|
|
51
|
+
next.axis === "child" &&
|
|
52
|
+
next.predicates.every((p) => isPositionIndependent(p, sc));
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* How many nodes of an axis a step needs: N for a first predicate that is
|
|
56
|
+
* the integer literal N, all of them otherwise.
|
|
57
|
+
* @param {import("../syntax/ast.js").Expr|undefined} predicate
|
|
58
|
+
* @returns {number}
|
|
59
|
+
*/
|
|
60
|
+
const positionalLimit = (predicate) =>
|
|
61
|
+
predicate?.type === "NumericLiteral" && predicate.kind === "integer"
|
|
62
|
+
? Math.max(Number(predicate.value), 0)
|
|
63
|
+
: Infinity;
|
|
64
|
+
|
|
65
|
+
/** Marks a `descendant-or-self::node()/child::T[P]` pair in a path. */
|
|
66
|
+
const GROUPED = "GroupedChildren";
|
|
67
|
+
|
|
68
|
+
/** Compilers by node type. */
|
|
69
|
+
export const pathCompilers = {
|
|
70
|
+
AxisStep(node, scope, compile) {
|
|
71
|
+
const axis = node.axis;
|
|
72
|
+
const test = compileNodeTest(node.nodeTest, node.axis, scope.sc);
|
|
73
|
+
const predicates = node.predicates.map((p) =>
|
|
74
|
+
compilePredicate(p, scope, compile),
|
|
75
|
+
);
|
|
76
|
+
const limit = positionalLimit(node.predicates[0]);
|
|
77
|
+
const reverse = REVERSE_AXES.has(node.axis);
|
|
78
|
+
return (ctx) => {
|
|
79
|
+
const item = contextNode(ctx, "XPTY0020");
|
|
80
|
+
let nodes = axisNodes(axis, item, test, limit, ctx.dyn);
|
|
81
|
+
for (const predicate of predicates) nodes = predicate(nodes, ctx);
|
|
82
|
+
return reverse ? nodes.reverse() : nodes;
|
|
83
|
+
};
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
PathExpr(node, scope, compile) {
|
|
87
|
+
const steps = [...node.steps];
|
|
88
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
89
|
+
if (isDescendantShortcut(steps[i], steps[i + 1], scope.sc)) {
|
|
90
|
+
steps.splice(i, 2, { ...steps[i + 1], axis: "descendant" });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
for (let i = 0; i < steps.length - 1; i++) {
|
|
94
|
+
if (isGroupedChildren(steps[i], steps[i + 1])) {
|
|
95
|
+
steps.splice(i, 2, { type: GROUPED, step: steps[i + 1] });
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const compiled = steps.map((step) => {
|
|
99
|
+
if (step.type === GROUPED) {
|
|
100
|
+
const run = compileGroupedChildren(step.step, scope, compile);
|
|
101
|
+
const evaluate = (ctx) => run([contextNode(ctx, "XPTY0020")], ctx);
|
|
102
|
+
return { evaluate, run };
|
|
103
|
+
}
|
|
104
|
+
const evaluate = compile(step, scope);
|
|
105
|
+
const axis = step.type === "AxisStep" ? step.axis : null;
|
|
106
|
+
return { evaluate, run: stepper(evaluate, axis) };
|
|
107
|
+
});
|
|
108
|
+
const sortedStart =
|
|
109
|
+
node.absolute ||
|
|
110
|
+
steps[0].type === "AxisStep" ||
|
|
111
|
+
steps[0].type === GROUPED;
|
|
112
|
+
const start = node.absolute
|
|
113
|
+
? (ctx) => {
|
|
114
|
+
const root = rootOf(contextNode(ctx, "XPTY0020"));
|
|
115
|
+
if (root.nodeType !== 9 && root.nodeType !== 11) {
|
|
116
|
+
throw new XPathError(
|
|
117
|
+
"XPDY0050",
|
|
118
|
+
"The root of the context node is not a document node",
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return [root];
|
|
122
|
+
}
|
|
123
|
+
: compiled.shift().evaluate;
|
|
124
|
+
return (ctx) => {
|
|
125
|
+
let current = start(ctx);
|
|
126
|
+
let sorted = sortedStart;
|
|
127
|
+
for (const { run } of compiled) {
|
|
128
|
+
current = run(current, ctx, sorted);
|
|
129
|
+
sorted = true;
|
|
130
|
+
}
|
|
131
|
+
return current;
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
FilterExpr(node, scope, compile) {
|
|
136
|
+
const base = compile(node.base, scope);
|
|
137
|
+
const predicate = compilePredicate(node.predicate, scope, compile);
|
|
138
|
+
return (ctx) => predicate(base(ctx), ctx);
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
SetExpr(node, scope, compile) {
|
|
142
|
+
const left = compile(node.left, scope);
|
|
143
|
+
const right = compile(node.right, scope);
|
|
144
|
+
const operator = node.operator;
|
|
145
|
+
const nodesOf = (sequence) => {
|
|
146
|
+
if (!sequence.every(isNode)) {
|
|
147
|
+
throw new XPathError(
|
|
148
|
+
"XPTY0004",
|
|
149
|
+
`The operands of ${operator} must be nodes`,
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
return sequence;
|
|
153
|
+
};
|
|
154
|
+
return (ctx) => {
|
|
155
|
+
const a = nodesOf(left(ctx));
|
|
156
|
+
const b = nodesOf(right(ctx));
|
|
157
|
+
if (operator === "union") return ctx.dyn.order.sort(a.concat(b));
|
|
158
|
+
const inRight = new Set(b);
|
|
159
|
+
const keep = operator === "intersect";
|
|
160
|
+
return ctx.dyn.order.sort(a.filter((n) => inRight.has(n) === keep));
|
|
161
|
+
};
|
|
162
|
+
},
|
|
163
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static test of whether a predicate depends on the context position or
|
|
3
|
+
* size: when it does not, `//T[P]` (`descendant-or-self::node()/child::T[P]`)
|
|
4
|
+
* selects the same nodes as `descendant::T[P]`, which needs no sorting.
|
|
5
|
+
*
|
|
6
|
+
* The test is syntactic and conservative: the predicate must evaluate to a
|
|
7
|
+
* boolean or to nodes (never to a number, which would select by position)
|
|
8
|
+
* and must not call position() or last() anywhere.
|
|
9
|
+
*
|
|
10
|
+
* @module @tradik/xslt3/xpath/eval/positional
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { NS } from "../../functions/signatures.js";
|
|
14
|
+
|
|
15
|
+
/** Expressions that always return a boolean or nodes. */
|
|
16
|
+
const NON_NUMERIC = new Set([
|
|
17
|
+
"ComparisonExpr",
|
|
18
|
+
"LogicalExpr",
|
|
19
|
+
"QuantifiedExpr",
|
|
20
|
+
"InstanceOfExpr",
|
|
21
|
+
"CastableExpr",
|
|
22
|
+
"AxisStep",
|
|
23
|
+
]);
|
|
24
|
+
|
|
25
|
+
/** Built-in functions returning xs:boolean. */
|
|
26
|
+
const BOOLEAN_FUNCTIONS = new Set([
|
|
27
|
+
"not",
|
|
28
|
+
"boolean",
|
|
29
|
+
"exists",
|
|
30
|
+
"empty",
|
|
31
|
+
"true",
|
|
32
|
+
"false",
|
|
33
|
+
"contains",
|
|
34
|
+
"starts-with",
|
|
35
|
+
"ends-with",
|
|
36
|
+
"matches",
|
|
37
|
+
"deep-equal",
|
|
38
|
+
]);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* @param {import("../syntax/ast.js").QName} name
|
|
42
|
+
* @param {import("./staticContext.js").StaticContext} sc
|
|
43
|
+
* @returns {boolean} whether the name is a built-in function name
|
|
44
|
+
*/
|
|
45
|
+
const isBuiltIn = (name, sc) =>
|
|
46
|
+
name.uri === null &&
|
|
47
|
+
(name.prefix === null
|
|
48
|
+
? sc.defaultFunctionNamespace === NS.fn
|
|
49
|
+
: sc.namespaces.get(name.prefix) === NS.fn);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Whether an AST contains a call or reference to position() or last().
|
|
53
|
+
* @param {*} node
|
|
54
|
+
* @returns {boolean}
|
|
55
|
+
*/
|
|
56
|
+
function usesFocusSize(node) {
|
|
57
|
+
if (Array.isArray(node)) return node.some(usesFocusSize);
|
|
58
|
+
if (node === null || typeof node !== "object") return false;
|
|
59
|
+
const named =
|
|
60
|
+
node.type === "FunctionCall" || node.type === "NamedFunctionRef";
|
|
61
|
+
if (named && ["position", "last"].includes(node.name.local)) return true;
|
|
62
|
+
return Object.values(node).some(usesFocusSize);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Whether a predicate selects the same items whatever the context
|
|
67
|
+
* position and size.
|
|
68
|
+
* @param {import("../syntax/ast.js").Expr} predicate
|
|
69
|
+
* @param {import("./staticContext.js").StaticContext} sc
|
|
70
|
+
* @returns {boolean}
|
|
71
|
+
*/
|
|
72
|
+
export function isPositionIndependent(predicate, sc) {
|
|
73
|
+
const { type } = predicate;
|
|
74
|
+
const nonNumeric =
|
|
75
|
+
NON_NUMERIC.has(type) ||
|
|
76
|
+
(type === "PathExpr" && predicate.steps.at(-1)?.type === "AxisStep") ||
|
|
77
|
+
(type === "FunctionCall" &&
|
|
78
|
+
BOOLEAN_FUNCTIONS.has(predicate.name.local) &&
|
|
79
|
+
isBuiltIn(predicate.name, sc));
|
|
80
|
+
return nonNumeric && !usesFocusSize(predicate);
|
|
81
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Predicates (XPath 3.1 section 3.3.3): a numeric predicate value selects
|
|
3
|
+
* the item at that position, any other value is taken as an effective
|
|
4
|
+
* boolean value. A literal integer predicate is compiled to an index.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xpath/eval/predicates
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isAtomic } from "../../xdm/atomic.js";
|
|
10
|
+
import { effectiveBooleanValue } from "../../xdm/nodes.js";
|
|
11
|
+
import { isNumericType } from "../../xdm/types.js";
|
|
12
|
+
import { withFocus } from "./scope.js";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A compiled predicate: filters a sequence (in its order, which gives the
|
|
16
|
+
* context positions).
|
|
17
|
+
* @typedef {(items: Array, ctx: import("./scope.js").Context) => Array} Predicate
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Whether a predicate value keeps the item at a position.
|
|
22
|
+
* @param {Array} value
|
|
23
|
+
* @param {number} position
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
export function predicateTruth(value, position) {
|
|
27
|
+
if (value.length === 1 && isAtomic(value[0])) {
|
|
28
|
+
const { type, value: number } = value[0];
|
|
29
|
+
if (isNumericType(type)) return Number(number) === position;
|
|
30
|
+
}
|
|
31
|
+
return effectiveBooleanValue(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {import("../syntax/ast.js").Expr} node - Predicate expression
|
|
36
|
+
* @param {import("./scope.js").Scope} scope
|
|
37
|
+
* @param {Function} compile
|
|
38
|
+
* @returns {Predicate}
|
|
39
|
+
*/
|
|
40
|
+
export function compilePredicate(node, scope, compile) {
|
|
41
|
+
if (node.type === "NumericLiteral" && node.kind === "integer") {
|
|
42
|
+
const index = Number(node.value) - 1;
|
|
43
|
+
return (items) =>
|
|
44
|
+
index >= 0 && index < items.length ? [items[index]] : [];
|
|
45
|
+
}
|
|
46
|
+
const predicate = compile(node, scope);
|
|
47
|
+
return (items, ctx) => {
|
|
48
|
+
const size = items.length;
|
|
49
|
+
const result = [];
|
|
50
|
+
for (let i = 0; i < size; i++) {
|
|
51
|
+
const value = predicate(withFocus(ctx, items[i], i + 1, size));
|
|
52
|
+
if (predicateTruth(value, i + 1)) result.push(items[i]);
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compilers of primary expressions: literals, variable references, the
|
|
3
|
+
* context item, the comma operator and the empty sequence.
|
|
4
|
+
*
|
|
5
|
+
* Evaluators return arrays that callers must not modify: a literal returns
|
|
6
|
+
* the same array every time.
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xpath/eval/primary
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { XPathError } from "../../errors.js";
|
|
12
|
+
import { AtomicValue } from "../../xdm/atomic.js";
|
|
13
|
+
import { Decimal } from "../../xdm/decimal.js";
|
|
14
|
+
import { types } from "../../xdm/types.js";
|
|
15
|
+
import { contextItem, variableDepth } from "./scope.js";
|
|
16
|
+
import { clark, namespaceOf } from "./staticContext.js";
|
|
17
|
+
|
|
18
|
+
const EMPTY = Object.freeze([]);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {import("../syntax/ast.js").NumericLiteral} node
|
|
22
|
+
* @returns {AtomicValue}
|
|
23
|
+
*/
|
|
24
|
+
function numericValue(node) {
|
|
25
|
+
if (node.kind === "integer") {
|
|
26
|
+
return new AtomicValue(types.integer, BigInt(node.value));
|
|
27
|
+
}
|
|
28
|
+
if (node.kind === "decimal") {
|
|
29
|
+
return new AtomicValue(types.decimal, Decimal.parse(node.value));
|
|
30
|
+
}
|
|
31
|
+
return new AtomicValue(types.double, Number(node.value));
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Evaluator of a variable reference resolved to a depth.
|
|
36
|
+
* @param {number} depth
|
|
37
|
+
* @param {string} name - For the error message
|
|
38
|
+
* @returns {import("./scope.js").Evaluator}
|
|
39
|
+
*/
|
|
40
|
+
function variableReader(depth, name) {
|
|
41
|
+
const check = (value) => {
|
|
42
|
+
if (value === undefined) {
|
|
43
|
+
throw new XPathError("XPDY0002", `No value is bound to $${name}`);
|
|
44
|
+
}
|
|
45
|
+
return value;
|
|
46
|
+
};
|
|
47
|
+
if (depth === 0) return (ctx) => check(ctx.env.value);
|
|
48
|
+
return (ctx) => {
|
|
49
|
+
let env = ctx.env;
|
|
50
|
+
for (let i = 0; i < depth; i++) env = env.next;
|
|
51
|
+
return check(env.value);
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Compilers by node type. */
|
|
56
|
+
export const primaryCompilers = {
|
|
57
|
+
NumericLiteral(node) {
|
|
58
|
+
const result = Object.freeze([numericValue(node)]);
|
|
59
|
+
return () => result;
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
StringLiteral(node) {
|
|
63
|
+
const result = Object.freeze([new AtomicValue(types.string, node.value)]);
|
|
64
|
+
return () => result;
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
EmptySequence() {
|
|
68
|
+
return () => EMPTY;
|
|
69
|
+
},
|
|
70
|
+
|
|
71
|
+
ContextItemExpr() {
|
|
72
|
+
return (ctx) => [contextItem(ctx)];
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
VarRef(node, scope) {
|
|
76
|
+
const key = clark(namespaceOf(node.name, scope.sc, ""), node.name.local);
|
|
77
|
+
const depth = variableDepth(scope, key);
|
|
78
|
+
if (depth < 0) {
|
|
79
|
+
throw new XPathError(
|
|
80
|
+
"XPST0008",
|
|
81
|
+
`The variable $${node.name.local} is not declared`,
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
return variableReader(depth, node.name.local);
|
|
85
|
+
},
|
|
86
|
+
|
|
87
|
+
SequenceExpr(node, scope, compile) {
|
|
88
|
+
const parts = node.items.map((item) => compile(item, scope));
|
|
89
|
+
return (ctx) => {
|
|
90
|
+
const result = [];
|
|
91
|
+
for (const part of parts) {
|
|
92
|
+
for (const item of part(ctx)) result.push(item);
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export { EMPTY };
|