@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
|
+
* xsl:merge, xsl:merge-source, xsl:merge-key and xsl:merge-action
|
|
3
|
+
* (XSLT 3.0 section 15): the static checks and the compiled sources; the
|
|
4
|
+
* merging itself is in runtime/merging.js.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/instructions/merge
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { atomize, stringValue } from "../../xdm/nodes.js";
|
|
10
|
+
import { types } from "../../xdm/types.js";
|
|
11
|
+
import { resolveUri } from "../../xpath/eval/uris.js";
|
|
12
|
+
import { useAccumulators } from "../compiler/useAccumulators.js";
|
|
13
|
+
import { checkAttributes, required, yesNo } from "../compiler/attributes.js";
|
|
14
|
+
import { compileBody } from "../compiler/body.js";
|
|
15
|
+
import { setApplicable } from "../runtime/accumulators.js";
|
|
16
|
+
import {
|
|
17
|
+
derive,
|
|
18
|
+
dynamicContextOf,
|
|
19
|
+
evaluate,
|
|
20
|
+
withFocus,
|
|
21
|
+
} from "../runtime/context.js";
|
|
22
|
+
import { BodyFrame, LoopFrame } from "../runtime/machine.js";
|
|
23
|
+
import { mergeGroups } from "../runtime/merging.js";
|
|
24
|
+
import { attr, isNCName, isXsl, xsltError } from "../names.js";
|
|
25
|
+
import { compileSortKey } from "./sort.js";
|
|
26
|
+
import { checkNoValidation } from "./sourceDocument.js";
|
|
27
|
+
|
|
28
|
+
/** Types a URI of for-each-source may have. */
|
|
29
|
+
const URI_TYPES = new Set([types.string, types.anyURI, types.untypedAtomic]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Compiles an xsl:merge-key.
|
|
33
|
+
* @param {Element} element
|
|
34
|
+
* @param {object} cx
|
|
35
|
+
* @param {object} scope
|
|
36
|
+
* @returns {object} the key, compiled as an xsl:sort
|
|
37
|
+
*/
|
|
38
|
+
function compileMergeKey(element, cx, scope) {
|
|
39
|
+
if (!isXsl(element, "merge-key")) {
|
|
40
|
+
throw xsltError("XTSE0010", "xsl:merge-source contains xsl:merge-key");
|
|
41
|
+
}
|
|
42
|
+
if (attr(element, "select") !== undefined && cx.children(element).length) {
|
|
43
|
+
throw xsltError("XTSE3200", "xsl:merge-key has select and content");
|
|
44
|
+
}
|
|
45
|
+
return compileSortKey(element, cx, scope);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The URIs of for-each-source as strings.
|
|
50
|
+
* @param {Array} items
|
|
51
|
+
* @returns {string[]}
|
|
52
|
+
*/
|
|
53
|
+
function uriStrings(items) {
|
|
54
|
+
return atomize(items).map((value) => {
|
|
55
|
+
if (!URI_TYPES.has(value.type)) {
|
|
56
|
+
throw xsltError("XPTY0004", "for-each-source must give URIs");
|
|
57
|
+
}
|
|
58
|
+
return stringValue(value);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Compiles an xsl:merge-source.
|
|
64
|
+
* @param {Element} element
|
|
65
|
+
* @param {object} cx
|
|
66
|
+
* @param {object} scope
|
|
67
|
+
* @param {number} index - Position among the sources
|
|
68
|
+
* @returns {object} `{name, keys, sortBeforeMerge, inputs(xc, machine)}`
|
|
69
|
+
*/
|
|
70
|
+
function compileMergeSource(element, cx, scope, index) {
|
|
71
|
+
checkAttributes(element);
|
|
72
|
+
const has = (name) => attr(element, name) !== undefined;
|
|
73
|
+
const forEachSource = has("for-each-source");
|
|
74
|
+
if (
|
|
75
|
+
(has("for-each-item") &&
|
|
76
|
+
(forEachSource || has("use-accumulators") || has("streamable"))) ||
|
|
77
|
+
(has("use-accumulators") && !forEachSource)
|
|
78
|
+
) {
|
|
79
|
+
throw xsltError("XTSE3195", "Conflicting attributes on xsl:merge-source");
|
|
80
|
+
}
|
|
81
|
+
if (!forEachSource && (has("validation") || has("type"))) {
|
|
82
|
+
throw xsltError("XTSE0010", "validation and type need for-each-source");
|
|
83
|
+
}
|
|
84
|
+
if (has("validation") && has("type")) {
|
|
85
|
+
throw xsltError("XTSE1505", "validation and type are exclusive");
|
|
86
|
+
}
|
|
87
|
+
checkNoValidation(element);
|
|
88
|
+
yesNo(element, "streamable", false);
|
|
89
|
+
const name = attr(element, "name")?.trim();
|
|
90
|
+
if (name !== undefined && !isNCName(name)) {
|
|
91
|
+
throw xsltError("XTSE0020", `Invalid merge source name ${name}`);
|
|
92
|
+
}
|
|
93
|
+
const xpath = (text) => cx.exprs.xpath(text, element, scope.vars);
|
|
94
|
+
const select = xpath(required(element, "select"));
|
|
95
|
+
const itemExpr = has("for-each-item")
|
|
96
|
+
? xpath(attr(element, "for-each-item"))
|
|
97
|
+
: null;
|
|
98
|
+
const sourceExpr = forEachSource
|
|
99
|
+
? xpath(attr(element, "for-each-source"))
|
|
100
|
+
: null;
|
|
101
|
+
const accumulators = useAccumulators(
|
|
102
|
+
attr(element, "use-accumulators") ?? "",
|
|
103
|
+
element,
|
|
104
|
+
cx,
|
|
105
|
+
);
|
|
106
|
+
const base = cx.baseUriOf(element);
|
|
107
|
+
const keys = cx
|
|
108
|
+
.children(element)
|
|
109
|
+
.map((child) => compileMergeKey(child, cx, scope));
|
|
110
|
+
if (keys.length === 0) {
|
|
111
|
+
throw xsltError("XTSE0010", "xsl:merge-source needs xsl:merge-key");
|
|
112
|
+
}
|
|
113
|
+
const documents = (xc) =>
|
|
114
|
+
uriStrings(evaluate(sourceExpr, xc)).map((uri) => {
|
|
115
|
+
const document = dynamicContextOf(xc).loadDocument(resolveUri(uri, base));
|
|
116
|
+
setApplicable(xc.tx, document, accumulators);
|
|
117
|
+
return document;
|
|
118
|
+
});
|
|
119
|
+
return {
|
|
120
|
+
name: name ?? `#${index}`,
|
|
121
|
+
named: name !== undefined,
|
|
122
|
+
keys,
|
|
123
|
+
sortBeforeMerge: yesNo(element, "sort-before-merge", false),
|
|
124
|
+
inputs(xc) {
|
|
125
|
+
let anchors = null;
|
|
126
|
+
if (itemExpr) anchors = evaluate(itemExpr, xc);
|
|
127
|
+
else if (sourceExpr) anchors = documents(xc);
|
|
128
|
+
if (!anchors) return [evaluate(select, xc)];
|
|
129
|
+
return anchors.map((anchor, i) =>
|
|
130
|
+
evaluate(select, withFocus(xc, anchor, i + 1, anchors.length)),
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* xsl:merge.
|
|
138
|
+
* @param {Element} element
|
|
139
|
+
* @param {object} cx
|
|
140
|
+
* @param {object} scope
|
|
141
|
+
* @returns {Function}
|
|
142
|
+
*/
|
|
143
|
+
export function compileMerge(element, cx, scope) {
|
|
144
|
+
const children = cx.children(element);
|
|
145
|
+
while (children.length > 0 && isXsl(children.at(-1), "fallback")) {
|
|
146
|
+
children.pop();
|
|
147
|
+
}
|
|
148
|
+
const action = children.at(-1);
|
|
149
|
+
const sourceElements = children.slice(0, -1);
|
|
150
|
+
if (
|
|
151
|
+
!action ||
|
|
152
|
+
!isXsl(action, "merge-action") ||
|
|
153
|
+
sourceElements.length === 0 ||
|
|
154
|
+
!sourceElements.every((child) => isXsl(child, "merge-source"))
|
|
155
|
+
) {
|
|
156
|
+
throw xsltError(
|
|
157
|
+
"XTSE0010",
|
|
158
|
+
"xsl:merge contains xsl:merge-source elements then xsl:merge-action",
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
const sources = sourceElements.map((child, i) =>
|
|
162
|
+
compileMergeSource(child, cx, scope, i),
|
|
163
|
+
);
|
|
164
|
+
const names = sources.filter((s) => s.named).map((s) => s.name);
|
|
165
|
+
if (new Set(names).size !== names.length) {
|
|
166
|
+
throw xsltError("XTSE3190", "Two merge sources have the same name");
|
|
167
|
+
}
|
|
168
|
+
if (sources.some((source) => source.keys.length !== sources[0].keys.length)) {
|
|
169
|
+
throw xsltError("XTSE2200", "The merge sources have different key counts");
|
|
170
|
+
}
|
|
171
|
+
checkAttributes(action);
|
|
172
|
+
const body = compileBody(action, cx, scope);
|
|
173
|
+
return (xc, out, machine) => {
|
|
174
|
+
const groups = mergeGroups(sources, xc, machine);
|
|
175
|
+
const size = groups.length;
|
|
176
|
+
if (size === 0 || body.length === 0) return;
|
|
177
|
+
machine.push(
|
|
178
|
+
new LoopFrame(size, (i) => {
|
|
179
|
+
const group = groups[i];
|
|
180
|
+
const context = derive(
|
|
181
|
+
withFocus(xc, group.entries[0].item, i + 1, size),
|
|
182
|
+
{
|
|
183
|
+
merge: { group, names },
|
|
184
|
+
},
|
|
185
|
+
);
|
|
186
|
+
machine.push(new BodyFrame(body, context, out));
|
|
187
|
+
}),
|
|
188
|
+
);
|
|
189
|
+
};
|
|
190
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xsl:number (XSLT 3.0 section 12): numbers from the value attribute or
|
|
3
|
+
* the position of a node, formatted with a format string of
|
|
4
|
+
* alphanumeric tokens and separators.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/instructions/number
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isNode } from "../../xdm/atomic.js";
|
|
10
|
+
import { canonicalString } from "../../xdm/lexical.js";
|
|
11
|
+
import { toNumber } from "../../xdm/generalCompare.js";
|
|
12
|
+
import { atomize } from "../../xdm/nodes.js";
|
|
13
|
+
import { infoOf } from "../compiler/elementInfo.js";
|
|
14
|
+
import { evaluate } from "../runtime/context.js";
|
|
15
|
+
import {
|
|
16
|
+
countKey,
|
|
17
|
+
numberAny,
|
|
18
|
+
numberHierarchy,
|
|
19
|
+
numberMemo,
|
|
20
|
+
sameKindAndName,
|
|
21
|
+
} from "../runtime/numbering.js";
|
|
22
|
+
import { formatNumbers } from "../runtime/numberFormat.js";
|
|
23
|
+
import { avtEvaluator } from "../runtime/values.js";
|
|
24
|
+
import { attr, xsltError } from "../names.js";
|
|
25
|
+
import { compilePattern, patternMatches } from "../patterns/compile.js";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts the value attribute's result to integers.
|
|
29
|
+
* @param {Array} values
|
|
30
|
+
* @param {boolean} compatible
|
|
31
|
+
* @returns {bigint[]|string} integers, or a string for invalid numbers
|
|
32
|
+
* in backwards-compatible mode
|
|
33
|
+
*/
|
|
34
|
+
function integersOf(values, compatible) {
|
|
35
|
+
const result = [];
|
|
36
|
+
const items = atomize(values);
|
|
37
|
+
// XSLT 1.0 behaviour: number(()) is NaN
|
|
38
|
+
if (compatible && items.length === 0) return "NaN";
|
|
39
|
+
// XSLT 1.0 behaviour: the first item only
|
|
40
|
+
for (const value of compatible ? items.slice(0, 1) : items) {
|
|
41
|
+
if (typeof value.value === "bigint" && value.value >= 0n) {
|
|
42
|
+
result.push(value.value);
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const n = toNumber(value).value;
|
|
46
|
+
if (!Number.isFinite(n) || n < -0.5) {
|
|
47
|
+
if (compatible) return canonicalString(toNumber(value));
|
|
48
|
+
throw xsltError("XTDE0980", `Cannot number ${canonicalString(value)}`);
|
|
49
|
+
}
|
|
50
|
+
result.push(BigInt(Math.round(n)));
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Checks a lang value: a language tag (xs:language).
|
|
57
|
+
* @param {string} text
|
|
58
|
+
* @param {string} code - XTSE0020 when static, XTDE0030 when evaluated
|
|
59
|
+
*/
|
|
60
|
+
function checkLanguage(text, code) {
|
|
61
|
+
if (!/^\s*[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\s*$/.test(text)) {
|
|
62
|
+
throw xsltError(code, `Invalid lang "${text}"`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Re-bases the numbers by the start-at attribute (XSLT 3.0 section 12.1).
|
|
68
|
+
* @param {bigint[]} numbers
|
|
69
|
+
* @param {string} text - Effective value of start-at
|
|
70
|
+
* @returns {bigint[]}
|
|
71
|
+
* @throws {import("../../errors.js").XPathError} XTDE0030 for a value
|
|
72
|
+
* that is not a list of integers
|
|
73
|
+
*/
|
|
74
|
+
function rebase(numbers, text) {
|
|
75
|
+
if (!/^\s*-?\d+(\s+-?\d+)*\s*$/.test(text)) {
|
|
76
|
+
throw xsltError("XTDE0030", `Invalid start-at "${text}"`);
|
|
77
|
+
}
|
|
78
|
+
const starts = text.trim().split(/\s+/).map(BigInt);
|
|
79
|
+
return numbers.map(
|
|
80
|
+
(number, i) => number + starts[Math.min(i, starts.length - 1)] - 1n,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Compiles an optional pattern attribute.
|
|
86
|
+
* @returns {object|null}
|
|
87
|
+
*/
|
|
88
|
+
function optionalPattern(element, name, cx, scope) {
|
|
89
|
+
const text = attr(element, name);
|
|
90
|
+
return text === undefined
|
|
91
|
+
? null
|
|
92
|
+
: compilePattern(text, element, cx, scope.vars);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Whether the numbers an instruction gives may be memoized: its count
|
|
97
|
+
* and from patterns must not read variables (local ones differ between
|
|
98
|
+
* invocations) or the dynamic context of the instruction.
|
|
99
|
+
* @param {Element} element
|
|
100
|
+
* @returns {boolean}
|
|
101
|
+
*/
|
|
102
|
+
function isMemoizable(element) {
|
|
103
|
+
const text = `${attr(element, "count") ?? ""} ${attr(element, "from") ?? ""}`;
|
|
104
|
+
return !/\$|current-|regex-group/.test(text);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* xsl:number.
|
|
109
|
+
* @param {Element} element
|
|
110
|
+
* @param {object} cx
|
|
111
|
+
* @param {object} scope
|
|
112
|
+
* @returns {Function}
|
|
113
|
+
*/
|
|
114
|
+
export function compileNumber(element, cx, scope) {
|
|
115
|
+
const expr = (name) => {
|
|
116
|
+
const text = attr(element, name);
|
|
117
|
+
return text === undefined
|
|
118
|
+
? null
|
|
119
|
+
: cx.exprs.xpath(text, element, scope.vars);
|
|
120
|
+
};
|
|
121
|
+
const avt = (name, fallback) => {
|
|
122
|
+
const text = attr(element, name);
|
|
123
|
+
return text === undefined
|
|
124
|
+
? () => fallback
|
|
125
|
+
: avtEvaluator(cx.exprs.avt(text, element, scope.vars));
|
|
126
|
+
};
|
|
127
|
+
const value = expr("value");
|
|
128
|
+
const select = expr("select");
|
|
129
|
+
const level = attr(element, "level")?.trim() ?? "single";
|
|
130
|
+
if (!["single", "multiple", "any"].includes(level)) {
|
|
131
|
+
throw xsltError("XTSE0020", `Invalid level ${level}`);
|
|
132
|
+
}
|
|
133
|
+
const count = optionalPattern(element, "count", cx, scope);
|
|
134
|
+
const from = optionalPattern(element, "from", cx, scope);
|
|
135
|
+
if (value && (select || count || from || attr(element, "level"))) {
|
|
136
|
+
throw xsltError(
|
|
137
|
+
"XTSE0975",
|
|
138
|
+
"xsl:number value excludes select, level, count, from",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
const format = avt("format", "1");
|
|
142
|
+
const ordinal = avt("ordinal", "");
|
|
143
|
+
const separator = avt("grouping-separator", "");
|
|
144
|
+
const size = avt("grouping-size", "");
|
|
145
|
+
const compatible = infoOf(element).version < 2;
|
|
146
|
+
const startAt =
|
|
147
|
+
attr(element, "start-at") === undefined ? null : avt("start-at");
|
|
148
|
+
const langText = attr(element, "lang");
|
|
149
|
+
if (langText !== undefined && !langText.includes("{")) {
|
|
150
|
+
checkLanguage(langText, "XTSE0020");
|
|
151
|
+
}
|
|
152
|
+
const lang = langText === undefined ? null : avt("lang");
|
|
153
|
+
// identity of the instruction for the memos of each transformation
|
|
154
|
+
const instruction = {};
|
|
155
|
+
const memoizable = isMemoizable(element);
|
|
156
|
+
return (xc, out) => {
|
|
157
|
+
let numbers;
|
|
158
|
+
if (value) {
|
|
159
|
+
numbers = integersOf(evaluate(value, xc), compatible);
|
|
160
|
+
if (typeof numbers === "string") {
|
|
161
|
+
out.text(numbers);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
const nodes = select ? evaluate(select, xc) : [xc.item];
|
|
166
|
+
if (nodes.length !== 1 || !isNode(nodes[0])) {
|
|
167
|
+
throw xsltError(
|
|
168
|
+
select ? "XTTE1000" : "XTTE0990",
|
|
169
|
+
"xsl:number needs one node to number",
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
const node = nodes[0];
|
|
173
|
+
const countTest = count
|
|
174
|
+
? (n) => patternMatches(count, n, xc)
|
|
175
|
+
: sameKindAndName(node);
|
|
176
|
+
const fromTest = from ? (n) => patternMatches(from, n, xc) : null;
|
|
177
|
+
const memo = memoizable
|
|
178
|
+
? numberMemo(xc.tx, instruction, count ? "" : countKey(node))
|
|
179
|
+
: null;
|
|
180
|
+
const places =
|
|
181
|
+
level === "any"
|
|
182
|
+
? numberAny(node, countTest, fromTest, memo)
|
|
183
|
+
: numberHierarchy(
|
|
184
|
+
node,
|
|
185
|
+
countTest,
|
|
186
|
+
fromTest,
|
|
187
|
+
level === "multiple",
|
|
188
|
+
memo,
|
|
189
|
+
);
|
|
190
|
+
numbers = places.map(BigInt);
|
|
191
|
+
}
|
|
192
|
+
if (startAt) numbers = rebase(numbers, startAt(xc));
|
|
193
|
+
const language = lang ? lang(xc) : null;
|
|
194
|
+
if (language !== null) checkLanguage(language, "XTDE0030");
|
|
195
|
+
const ordinalText = ordinal(xc).trim();
|
|
196
|
+
out.text(
|
|
197
|
+
formatNumbers(numbers, format(xc), {
|
|
198
|
+
ordinal: ordinalText !== "" && ordinalText !== "no" && ordinalText,
|
|
199
|
+
lang: language,
|
|
200
|
+
separator: separator(xc),
|
|
201
|
+
size: Number(size(xc)),
|
|
202
|
+
}),
|
|
203
|
+
);
|
|
204
|
+
};
|
|
205
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xsl:message (XSLT 3.0 section 22.1): messages collected as documents,
|
|
3
|
+
* optionally terminating the transformation.
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/xslt/instructions/output
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { stringItem } from "../../xpath/eval/atomics.js";
|
|
9
|
+
import { compileBody } from "../compiler/body.js";
|
|
10
|
+
import { evaluate } from "../runtime/context.js";
|
|
11
|
+
import { TreeReceiver } from "../runtime/treeReceiver.js";
|
|
12
|
+
import { avtEvaluator, bodySequence } from "../runtime/values.js";
|
|
13
|
+
import { attr, isNCName, isXsl, xsltError } from "../names.js";
|
|
14
|
+
|
|
15
|
+
/** Values of yes/no attributes: the "yes" ones first. */
|
|
16
|
+
const YES_NO = ["yes", "true", "1", "no", "false", "0"];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Builds a document node holding items.
|
|
20
|
+
* @param {Array} items
|
|
21
|
+
* @param {object} xc
|
|
22
|
+
* @returns {Node} a document fragment
|
|
23
|
+
*/
|
|
24
|
+
function buildDocument(items, xc) {
|
|
25
|
+
const fragment = xc.tx.scratch().createDocumentFragment();
|
|
26
|
+
const out = new TreeReceiver(fragment, new Map());
|
|
27
|
+
for (const item of items) out.item(item);
|
|
28
|
+
return fragment;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* xsl:message.
|
|
33
|
+
* @param {Element} element
|
|
34
|
+
* @param {object} cx
|
|
35
|
+
* @param {object} scope
|
|
36
|
+
* @returns {Function}
|
|
37
|
+
*/
|
|
38
|
+
export function compileMessage(element, cx, scope) {
|
|
39
|
+
const selectText = attr(element, "select");
|
|
40
|
+
const select =
|
|
41
|
+
selectText === undefined
|
|
42
|
+
? null
|
|
43
|
+
: cx.exprs.xpath(selectText, element, scope.vars);
|
|
44
|
+
const body = compileBody(
|
|
45
|
+
element,
|
|
46
|
+
cx,
|
|
47
|
+
scope,
|
|
48
|
+
cx.children(element).filter((child) => !isXsl(child, "fallback")),
|
|
49
|
+
);
|
|
50
|
+
const terminateText = attr(element, "terminate");
|
|
51
|
+
if (
|
|
52
|
+
terminateText !== undefined &&
|
|
53
|
+
!terminateText.includes("{") &&
|
|
54
|
+
!YES_NO.includes(terminateText.trim())
|
|
55
|
+
) {
|
|
56
|
+
throw xsltError("XTSE0020", `Invalid terminate value ${terminateText}`);
|
|
57
|
+
}
|
|
58
|
+
const terminate = terminateText
|
|
59
|
+
? avtEvaluator(cx.exprs.avt(terminateText, element, scope.vars))
|
|
60
|
+
: () => "no";
|
|
61
|
+
const codeText = attr(element, "error-code");
|
|
62
|
+
const errorCode = codeText
|
|
63
|
+
? avtEvaluator(cx.exprs.avt(codeText, element, scope.vars))
|
|
64
|
+
: () => "XTMM9000";
|
|
65
|
+
return (xc, out, machine) => {
|
|
66
|
+
let items;
|
|
67
|
+
try {
|
|
68
|
+
items = [
|
|
69
|
+
...(select ? evaluate(select, xc) : []),
|
|
70
|
+
...bodySequence(body, xc, machine, false),
|
|
71
|
+
];
|
|
72
|
+
} catch (error) {
|
|
73
|
+
if (typeof error?.code !== "string") throw error;
|
|
74
|
+
// XSLT 3.0 section 23.1: an error in the content of a message is
|
|
75
|
+
// reported in the message, not by failing the transformation
|
|
76
|
+
items = [stringItem(`Error in xsl:message: ${error.message}`)];
|
|
77
|
+
}
|
|
78
|
+
let message;
|
|
79
|
+
try {
|
|
80
|
+
message = buildDocument(items, xc);
|
|
81
|
+
} catch {
|
|
82
|
+
// XSLT 3.0: a message that cannot be a document does not fail
|
|
83
|
+
message = items;
|
|
84
|
+
}
|
|
85
|
+
xc.tx.messages.push(message);
|
|
86
|
+
xc.tx.onMessage?.(message);
|
|
87
|
+
const value = terminate(xc).trim();
|
|
88
|
+
if (!YES_NO.includes(value)) {
|
|
89
|
+
throw xsltError("XTDE0030", `Invalid terminate value ${value}`);
|
|
90
|
+
}
|
|
91
|
+
if (YES_NO.indexOf(value) < 3) {
|
|
92
|
+
const code = errorCode(xc).trim();
|
|
93
|
+
const local = code.replace(/^Q\{[^}]*\}/, "").replace(/^[^:]*:/, "");
|
|
94
|
+
const error = xsltError(
|
|
95
|
+
isNCName(local) ? local : "XTMM9000",
|
|
96
|
+
"Terminated by xsl:message",
|
|
97
|
+
);
|
|
98
|
+
error.value = message;
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parameters (XSLT 3.0 sections 9.2 and 10.1.1): the xsl:param
|
|
3
|
+
* declarations of templates and functions, and the xsl:with-param
|
|
4
|
+
* children of invocations.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/instructions/params
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { checkAttributes, yesNo } from "../compiler/attributes.js";
|
|
10
|
+
import { displayName, isXsl, xsltError } from "../names.js";
|
|
11
|
+
import { compileValue, typeConverter, variableName } from "./variables.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Compiles the xsl:with-param children of an instruction.
|
|
15
|
+
* @param {Node[]} children
|
|
16
|
+
* @param {object} cx
|
|
17
|
+
* @param {object} scope
|
|
18
|
+
* @returns {{names: Set<string>,
|
|
19
|
+
* evaluate: (xc: object, machine: object) => import("../runtime/apply.js").Args}}
|
|
20
|
+
*/
|
|
21
|
+
export function compileWithParams(children, cx, scope) {
|
|
22
|
+
const compiled = [];
|
|
23
|
+
for (const child of children) {
|
|
24
|
+
if (!isXsl(child, "with-param")) continue;
|
|
25
|
+
checkAttributes(child);
|
|
26
|
+
const key = variableName(child, cx);
|
|
27
|
+
if (compiled.some((param) => param.key === key)) {
|
|
28
|
+
throw xsltError("XTSE0670", `Duplicate parameter ${key}`);
|
|
29
|
+
}
|
|
30
|
+
const { value } = compileValue(child, cx, scope, "XTTE0570");
|
|
31
|
+
compiled.push({ key, value, tunnel: yesNo(child, "tunnel", false) });
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
names: new Set(compiled.filter((p) => !p.tunnel).map((p) => p.key)),
|
|
35
|
+
evaluate(xc, machine) {
|
|
36
|
+
const params = new Map();
|
|
37
|
+
const tunnel = new Map();
|
|
38
|
+
for (const param of compiled) {
|
|
39
|
+
const target = param.tunnel ? tunnel : params;
|
|
40
|
+
target.set(param.key, param.value(xc, machine));
|
|
41
|
+
}
|
|
42
|
+
return { params, tunnel };
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The default value of a parameter with a type and no default: the
|
|
49
|
+
* empty sequence, or XTDE0610 when the type does not allow it.
|
|
50
|
+
* @param {object} compiled - See variables.compileValue
|
|
51
|
+
* @param {string} key
|
|
52
|
+
* @returns {(xc: object, machine: object) => Array}
|
|
53
|
+
*/
|
|
54
|
+
function implicitDefault(compiled, key) {
|
|
55
|
+
return (xc, machine) => {
|
|
56
|
+
try {
|
|
57
|
+
return compiled.value(xc, machine);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw error.code === "XTTE0590"
|
|
60
|
+
? xsltError("XTDE0610", `No value for the parameter ${key}`)
|
|
61
|
+
: error;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The parameters of a template or function, compiled in order (each
|
|
68
|
+
* default value sees the parameters before it).
|
|
69
|
+
* @param {Element[]} elements - The xsl:param children
|
|
70
|
+
* @param {object} cx
|
|
71
|
+
* @param {object} scope
|
|
72
|
+
* @param {boolean} [isFunction]
|
|
73
|
+
* @returns {{params: object[], scope: object}}
|
|
74
|
+
*/
|
|
75
|
+
export function compileParams(elements, cx, scope, isFunction = false) {
|
|
76
|
+
const params = [];
|
|
77
|
+
let current = scope;
|
|
78
|
+
for (const element of elements) {
|
|
79
|
+
checkAttributes(element);
|
|
80
|
+
const key = variableName(element, cx);
|
|
81
|
+
if (params.some((param) => param.key === key)) {
|
|
82
|
+
throw xsltError("XTSE0580", `Duplicate parameter ${key}`);
|
|
83
|
+
}
|
|
84
|
+
const compiled = compileValue(element, cx, current, "XTTE0590");
|
|
85
|
+
if (isFunction && compiled.hasDefault) {
|
|
86
|
+
throw xsltError("XTSE0760", "A function parameter cannot have a default");
|
|
87
|
+
}
|
|
88
|
+
const required = isFunction || yesNo(element, "required", false);
|
|
89
|
+
if (!isFunction && required && compiled.hasDefault) {
|
|
90
|
+
throw xsltError("XTSE0010", "A required parameter cannot have a default");
|
|
91
|
+
}
|
|
92
|
+
params.push({
|
|
93
|
+
key,
|
|
94
|
+
required,
|
|
95
|
+
tunnel: yesNo(element, "tunnel", false),
|
|
96
|
+
value:
|
|
97
|
+
compiled.hasDefault || !compiled.type
|
|
98
|
+
? compiled.value
|
|
99
|
+
: implicitDefault(compiled, key),
|
|
100
|
+
type: compiled.type,
|
|
101
|
+
convert: typeConverter(
|
|
102
|
+
compiled.type,
|
|
103
|
+
"XTTE0590",
|
|
104
|
+
`parameter $${displayName(key)}`,
|
|
105
|
+
),
|
|
106
|
+
});
|
|
107
|
+
current = { ...current, vars: { key, next: current.vars } };
|
|
108
|
+
}
|
|
109
|
+
return { params, scope: current };
|
|
110
|
+
}
|