@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,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xsl:global-context-item (XSLT 3.0 section 3.5.6): whether the global
|
|
3
|
+
* context item is required, optional or absent, and its type, checked
|
|
4
|
+
* when a transformation starts.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/compiler/globalContextItem
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { coerce } from "../../xpath/eval/coercion.js";
|
|
10
|
+
import { attr, xsltError } from "../names.js";
|
|
11
|
+
|
|
12
|
+
/** Values of the use attribute. */
|
|
13
|
+
const USES = new Set(["required", "optional", "absent"]);
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Compiles the xsl:global-context-item declarations into
|
|
17
|
+
* `cx.globalContextItem`: `{use, type}`.
|
|
18
|
+
* @param {object[]} declarations
|
|
19
|
+
* @param {object} cx
|
|
20
|
+
* @throws {import("../../errors.js").XPathError} XTSE3087 for two
|
|
21
|
+
* different declarations, XTSE0020 for an invalid use, XTSE3089 for a
|
|
22
|
+
* type with use="absent"
|
|
23
|
+
*/
|
|
24
|
+
export function declareGlobalContextItem(declarations, cx) {
|
|
25
|
+
cx.globalContextItem = { use: "optional", type: null };
|
|
26
|
+
const seen = new Set();
|
|
27
|
+
const modules = new Set();
|
|
28
|
+
for (const { element } of declarations) {
|
|
29
|
+
if (modules.has(element.parentNode)) {
|
|
30
|
+
throw xsltError("XTSE3087", "Two xsl:global-context-item in a module");
|
|
31
|
+
}
|
|
32
|
+
modules.add(element.parentNode);
|
|
33
|
+
const use = (attr(element, "use") ?? "optional").trim();
|
|
34
|
+
if (!USES.has(use)) throw xsltError("XTSE0020", `Invalid use "${use}"`);
|
|
35
|
+
const asText = attr(element, "as");
|
|
36
|
+
if (use === "absent" && asText !== undefined) {
|
|
37
|
+
throw xsltError("XTSE3089", 'use="absent" cannot have a type');
|
|
38
|
+
}
|
|
39
|
+
// the types compared without whitespace, which is not significant
|
|
40
|
+
seen.add(`${use}|${asText?.replace(/\s+/g, "")}`);
|
|
41
|
+
if (seen.size > 1) {
|
|
42
|
+
throw xsltError("XTSE3087", "Conflicting xsl:global-context-item");
|
|
43
|
+
}
|
|
44
|
+
const type =
|
|
45
|
+
asText === undefined ? null : cx.exprs.sequenceType(asText, element);
|
|
46
|
+
cx.globalContextItem = { use, type };
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Checks the used packages: their global context item is absent, so one
|
|
52
|
+
* that declares it required cannot run (XSLT 3.0 xsl:global-context-item:
|
|
53
|
+
* XTTE0590).
|
|
54
|
+
* @param {object} cx - The top-level package's compiler
|
|
55
|
+
* @throws {import("../../errors.js").XPathError} XTTE0590
|
|
56
|
+
*/
|
|
57
|
+
export function checkLibraryContextItems(cx) {
|
|
58
|
+
for (const { child } of cx.uses) {
|
|
59
|
+
if (child.globalContextItem.use === "required") {
|
|
60
|
+
throw xsltError(
|
|
61
|
+
"XTTE0590",
|
|
62
|
+
`The used package ${child.packageName} requires a global context item`,
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
checkLibraryContextItems(child);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The global context item of a transformation, checked against the
|
|
71
|
+
* declaration.
|
|
72
|
+
* @param {object} declared - `{use, type}`
|
|
73
|
+
* @param {*} item - The item supplied, undefined for none
|
|
74
|
+
* @returns {*} the item, undefined when absent
|
|
75
|
+
* @throws {import("../../errors.js").XPathError} XTDE3086 when a
|
|
76
|
+
* required item is missing, XTTE0590 when it has the wrong type
|
|
77
|
+
*/
|
|
78
|
+
export function globalContextItem(declared, item) {
|
|
79
|
+
if (declared.use === "absent") return undefined;
|
|
80
|
+
if (item === undefined) {
|
|
81
|
+
if (declared.use === "required") {
|
|
82
|
+
throw xsltError("XTDE3086", "A global context item is required");
|
|
83
|
+
}
|
|
84
|
+
return undefined;
|
|
85
|
+
}
|
|
86
|
+
if (!declared.type) return item;
|
|
87
|
+
try {
|
|
88
|
+
return coerce([item], declared.type, { what: "global context item" })[0];
|
|
89
|
+
} catch (error) {
|
|
90
|
+
throw xsltError("XTTE0590", error.message.replace(/^[A-Z]+\d+: /, ""));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global variables and parameters (XSLT 3.0 section 9.5) and stylesheet
|
|
3
|
+
* functions (section 10.3): names collected first (they are in scope
|
|
4
|
+
* everywhere), the declaration of highest import precedence chosen, then
|
|
5
|
+
* compiled.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/compiler/globals
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { coerce } from "../../xpath/eval/coercion.js";
|
|
11
|
+
import { compileParams } from "../instructions/params.js";
|
|
12
|
+
import {
|
|
13
|
+
compileValue,
|
|
14
|
+
typeConverter,
|
|
15
|
+
variableName,
|
|
16
|
+
} from "../instructions/variables.js";
|
|
17
|
+
import {
|
|
18
|
+
attr,
|
|
19
|
+
displayName,
|
|
20
|
+
isXsl,
|
|
21
|
+
RESERVED_NAMESPACES,
|
|
22
|
+
xsltError,
|
|
23
|
+
} from "../names.js";
|
|
24
|
+
import { required, yesNo } from "./attributes.js";
|
|
25
|
+
import { compileBody } from "./body.js";
|
|
26
|
+
import { splitParams } from "./templatesDecl.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Chooses, among declarations with the same key, the one of highest
|
|
30
|
+
* import precedence.
|
|
31
|
+
* @param {object[]} declarations
|
|
32
|
+
* @param {(declaration: object) => string} keyOf
|
|
33
|
+
* @param {string} code - Error code for two at the same precedence
|
|
34
|
+
* @returns {Map<string, object>} the winners by key, in declaration order
|
|
35
|
+
*/
|
|
36
|
+
export function winners(declarations, keyOf, code) {
|
|
37
|
+
const chosen = new Map();
|
|
38
|
+
for (const declaration of declarations) {
|
|
39
|
+
const key = keyOf(declaration);
|
|
40
|
+
const current = chosen.get(key);
|
|
41
|
+
if (current && current.precedence === declaration.precedence) {
|
|
42
|
+
throw xsltError(code, `Duplicate declaration of ${key}`);
|
|
43
|
+
}
|
|
44
|
+
if (!current || current.precedence < declaration.precedence) {
|
|
45
|
+
chosen.set(key, declaration);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return chosen;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Collects the global variables and parameters: their keys, in scope
|
|
53
|
+
* everywhere, and the winning declarations.
|
|
54
|
+
* @param {object[]} declarations - xsl:variable and xsl:param declarations
|
|
55
|
+
* @param {object} cx
|
|
56
|
+
* @returns {Map<string, object>}
|
|
57
|
+
*/
|
|
58
|
+
export function collectGlobals(declarations, cx) {
|
|
59
|
+
return winners(
|
|
60
|
+
declarations,
|
|
61
|
+
(declaration) => {
|
|
62
|
+
declaration.key ??= variableName(declaration.element, cx);
|
|
63
|
+
return declaration.key;
|
|
64
|
+
},
|
|
65
|
+
"XTSE0630",
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* A scope without a variable: its binding keeps its place (references are
|
|
71
|
+
* resolved to depths) but cannot be found by name.
|
|
72
|
+
* @param {object} scope - `{vars}`
|
|
73
|
+
* @param {string} key - Clark name hidden
|
|
74
|
+
* @returns {object} the scope
|
|
75
|
+
*/
|
|
76
|
+
function hideVariable(scope, key) {
|
|
77
|
+
const before = [];
|
|
78
|
+
let found = scope.vars;
|
|
79
|
+
while (found !== null && found.key !== key) {
|
|
80
|
+
before.push(found.key);
|
|
81
|
+
found = found.next;
|
|
82
|
+
}
|
|
83
|
+
if (found === null) return scope;
|
|
84
|
+
let vars = { key: "", next: found.next };
|
|
85
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
86
|
+
vars = { key: before[i], next: vars };
|
|
87
|
+
}
|
|
88
|
+
return { ...scope, vars };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Compiles a global variable or parameter. Its own name is not in scope
|
|
93
|
+
* in its declaration (XSLT 3.0 section 9.9: XPST0008).
|
|
94
|
+
* @param {{element: Element, key: string}} declaration
|
|
95
|
+
* @param {object} cx
|
|
96
|
+
* @param {object} [global] - Default: the global scope of the package
|
|
97
|
+
* @returns {object} `{key, isParam, required, value, convert}`
|
|
98
|
+
*/
|
|
99
|
+
export function compileGlobal({ element, key }, cx, global = cx.globalScope()) {
|
|
100
|
+
const scope = hideVariable(global, key);
|
|
101
|
+
const isParam = isXsl(element, "param");
|
|
102
|
+
if (isParam && yesNo(element, "tunnel", false)) {
|
|
103
|
+
throw xsltError(
|
|
104
|
+
"XTSE0020",
|
|
105
|
+
"A global parameter cannot be a tunnel parameter",
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
const compiled = compileValue(
|
|
109
|
+
element,
|
|
110
|
+
cx,
|
|
111
|
+
scope,
|
|
112
|
+
isParam ? "XTTE0590" : "XTTE0570",
|
|
113
|
+
);
|
|
114
|
+
return {
|
|
115
|
+
key,
|
|
116
|
+
isParam,
|
|
117
|
+
required: isParam && yesNo(element, "required", false),
|
|
118
|
+
// a static variable keeps the value of the static stage
|
|
119
|
+
value:
|
|
120
|
+
cx.staticDeclarations?.get(key)?.element === element
|
|
121
|
+
? () =>
|
|
122
|
+
typeConverter(
|
|
123
|
+
compiled.type,
|
|
124
|
+
isParam ? "XTTE0590" : "XTTE0570",
|
|
125
|
+
`value of $${displayName(key)}`,
|
|
126
|
+
)(cx.statics.get(key))
|
|
127
|
+
: compiled.value,
|
|
128
|
+
convert: typeConverter(
|
|
129
|
+
compiled.type,
|
|
130
|
+
"XTTE0590",
|
|
131
|
+
`parameter $${displayName(key)}`,
|
|
132
|
+
),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The function definition of an xsl:function for the function library;
|
|
138
|
+
* its body is compiled later by {@link compileFunction}.
|
|
139
|
+
* @param {object} declaration
|
|
140
|
+
* @param {object} cx
|
|
141
|
+
* @returns {object} `{key, uri, local, arity, declaration}`
|
|
142
|
+
*/
|
|
143
|
+
export function functionSignature(declaration, cx) {
|
|
144
|
+
const { element } = declaration;
|
|
145
|
+
const name = cx.exprs.qname(required(element, "name"), element, {
|
|
146
|
+
syntaxCode: "XTSE0020",
|
|
147
|
+
});
|
|
148
|
+
yesNo(element, "override", true);
|
|
149
|
+
yesNo(element, "override-extension-function", true);
|
|
150
|
+
if (!name.uri) throw xsltError("XTSE0740", "A function name needs a prefix");
|
|
151
|
+
if (RESERVED_NAMESPACES.has(name.uri)) {
|
|
152
|
+
throw xsltError("XTSE0080", `The namespace ${name.uri} is reserved`);
|
|
153
|
+
}
|
|
154
|
+
const children = cx.children(element);
|
|
155
|
+
const arity = splitParams(children).params.length;
|
|
156
|
+
return {
|
|
157
|
+
key: `{${name.uri}}${name.local}#${arity}`,
|
|
158
|
+
...name,
|
|
159
|
+
arity,
|
|
160
|
+
declaration,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Compiles the body of a stylesheet function.
|
|
166
|
+
* @param {object} signature - From {@link functionSignature}
|
|
167
|
+
* @param {object} cx
|
|
168
|
+
* @returns {object} `{params, body, convert}`
|
|
169
|
+
*/
|
|
170
|
+
export function compileFunction(signature, cx) {
|
|
171
|
+
const { element } = signature.declaration;
|
|
172
|
+
const { params: paramElements, rest } = splitParams(cx.children(element));
|
|
173
|
+
const { params, scope } = compileParams(
|
|
174
|
+
paramElements,
|
|
175
|
+
cx,
|
|
176
|
+
cx.globalScope(),
|
|
177
|
+
true,
|
|
178
|
+
);
|
|
179
|
+
const asText = attr(element, "as");
|
|
180
|
+
const type = asText ? cx.exprs.sequenceType(asText, element) : null;
|
|
181
|
+
return {
|
|
182
|
+
params: params.map((param) => ({
|
|
183
|
+
...param,
|
|
184
|
+
convert: param.type
|
|
185
|
+
? (value) =>
|
|
186
|
+
coerce(value, param.type, {
|
|
187
|
+
what: `argument $${displayName(param.key)}`,
|
|
188
|
+
})
|
|
189
|
+
: (value) => value,
|
|
190
|
+
})),
|
|
191
|
+
body: compileBody(element, cx, { ...scope, inFunction: true }, rest),
|
|
192
|
+
convert: typeConverter(type, "XTTE0780", "result of the function"),
|
|
193
|
+
memo: isMemoized(element),
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether the results of a function are cached: cache="yes", or
|
|
199
|
+
* new-each-time="no" (the same arguments give the same nodes).
|
|
200
|
+
* @param {Element} element - xsl:function
|
|
201
|
+
* @returns {boolean}
|
|
202
|
+
*/
|
|
203
|
+
function isMemoized(element) {
|
|
204
|
+
const flag = (name) => attr(element, name)?.trim();
|
|
205
|
+
return (
|
|
206
|
+
["yes", "true", "1"].includes(flag("cache")) ||
|
|
207
|
+
["no", "false", "0"].includes(flag("new-each-time"))
|
|
208
|
+
);
|
|
209
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Linking the packages of a stylesheet (XSLT 3.0 section 3.5.3.5): one
|
|
3
|
+
* environment for the global variables of every package, the components
|
|
4
|
+
* accepted from used packages entered in the registries of the using
|
|
5
|
+
* package, and abstract components made to fail when invoked (overriding
|
|
6
|
+
* declarations: see overrideLinks.js).
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/compiler/linker
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { xsltError } from "../names.js";
|
|
12
|
+
import { compileGlobal } from "./globals.js";
|
|
13
|
+
import { usesOriginal } from "./overrideLinks.js";
|
|
14
|
+
|
|
15
|
+
/** A body that raises XTDE3052: an abstract component was invoked. */
|
|
16
|
+
const ABSTRACT_BODY = [
|
|
17
|
+
() => {
|
|
18
|
+
throw xsltError("XTDE3052", "An abstract component was invoked");
|
|
19
|
+
},
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Lays out the global variables of every package of the stylesheet in
|
|
24
|
+
* one environment, and gives each package the names it sees.
|
|
25
|
+
* @param {object} top - The top-level package's compiler
|
|
26
|
+
*/
|
|
27
|
+
export function layoutGlobals(top) {
|
|
28
|
+
const slots = [];
|
|
29
|
+
const packages = [];
|
|
30
|
+
const visit = (cx) => {
|
|
31
|
+
for (const use of cx.uses) visit(use.child);
|
|
32
|
+
packages.push(cx);
|
|
33
|
+
slots.push(...cx.ownSlots);
|
|
34
|
+
};
|
|
35
|
+
visit(top);
|
|
36
|
+
for (const cx of packages) {
|
|
37
|
+
cx.layout = slots;
|
|
38
|
+
cx.setGlobals(
|
|
39
|
+
slots.map((slot, i) =>
|
|
40
|
+
cx.globalNames.get(slot.name) === slot ? slot.name : `\u0000${i}`,
|
|
41
|
+
),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Evaluates a global variable of a library package with an absent focus.
|
|
48
|
+
* @param {object} compiled - Compiled global
|
|
49
|
+
*/
|
|
50
|
+
function withoutFocus(compiled) {
|
|
51
|
+
const { value } = compiled;
|
|
52
|
+
compiled.value = (xc, machine) =>
|
|
53
|
+
value({ ...xc, item: undefined, position: 0, size: 0 }, machine);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Compiles the global variables whose value this package gives.
|
|
58
|
+
* @param {object} cx
|
|
59
|
+
* @returns {object[]} compiled globals of the whole layout (meaningful
|
|
60
|
+
* for the top-level package, once every package is compiled)
|
|
61
|
+
*/
|
|
62
|
+
export function compileSlots(cx) {
|
|
63
|
+
cx.layout.forEach((slot) => {
|
|
64
|
+
if (slot.owner !== cx) return;
|
|
65
|
+
const original = slot.original
|
|
66
|
+
? cx.layout.indexOf(slot.original)
|
|
67
|
+
: undefined;
|
|
68
|
+
if (slot.original?.abstract && usesOriginal(slot.declaration.element)) {
|
|
69
|
+
throw xsltError("XTSE3075", "The overridden variable is abstract");
|
|
70
|
+
}
|
|
71
|
+
slot.compiled = compileGlobal(
|
|
72
|
+
slot.declaration,
|
|
73
|
+
cx,
|
|
74
|
+
cx.globalScope(original),
|
|
75
|
+
);
|
|
76
|
+
// the value an overriding variable replaced is only read through it
|
|
77
|
+
if (slot.name === "") slot.compiled.required = false;
|
|
78
|
+
// the global context item belongs to the top-level package
|
|
79
|
+
if (cx.packageChain.length > 0) withoutFocus(slot.compiled);
|
|
80
|
+
if (slot.abstract) {
|
|
81
|
+
slot.compiled.value = ABSTRACT_BODY[0];
|
|
82
|
+
slot.compiled.required = false;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return cx.layout.map((slot, i) => ({
|
|
86
|
+
...slot.compiled,
|
|
87
|
+
key: cx.globalKeys[i],
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Enters the templates, attribute sets and modes accepted from used
|
|
93
|
+
* packages in the registries of this package.
|
|
94
|
+
* @param {object} cx
|
|
95
|
+
*/
|
|
96
|
+
export function importAccepted(cx) {
|
|
97
|
+
for (const use of cx.uses) {
|
|
98
|
+
for (const { component } of use.accepted) {
|
|
99
|
+
const { owner, key } = component;
|
|
100
|
+
if (component.kind === "template") {
|
|
101
|
+
cx.namedTemplates.set(key, owner.namedTemplates.get(key));
|
|
102
|
+
} else if (component.kind === "attribute-set") {
|
|
103
|
+
cx.attributeSets.set(key, owner.attributeSets.get(key));
|
|
104
|
+
} else if (component.kind === "mode") {
|
|
105
|
+
cx.acceptedModes.set(key, owner.mode(key));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Makes the abstract components of this package raise XTDE3052.
|
|
113
|
+
* @param {object} cx
|
|
114
|
+
*/
|
|
115
|
+
export function markAbstract(cx) {
|
|
116
|
+
for (const entry of cx.table.values()) {
|
|
117
|
+
if (entry.owner !== cx || entry.visibility !== "abstract") continue;
|
|
118
|
+
if (entry.kind === "template") {
|
|
119
|
+
const template = cx.namedTemplates.get(entry.key);
|
|
120
|
+
template.body = ABSTRACT_BODY;
|
|
121
|
+
// its parameters are not evaluated: the call fails first
|
|
122
|
+
template.params = template.params.map((param) => ({
|
|
123
|
+
...param,
|
|
124
|
+
required: false,
|
|
125
|
+
value: () => [],
|
|
126
|
+
}));
|
|
127
|
+
} else if (entry.kind === "function") {
|
|
128
|
+
entry.cell.body = ABSTRACT_BODY;
|
|
129
|
+
} else if (entry.kind === "attribute-set") {
|
|
130
|
+
const parts = cx.attributeSets.get(entry.key);
|
|
131
|
+
parts.splice(0, parts.length, { uses: [], body: ABSTRACT_BODY, cx });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Other declarations: xsl:strip-space and xsl:preserve-space (XSLT 3.0
|
|
3
|
+
* section 4.3), xsl:namespace-alias (11.1.3) and xsl:key (20.2.1);
|
|
4
|
+
* xsl:mode is in modeDecl.js.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/compiler/miscDecl
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { compileBody } from "./body.js";
|
|
10
|
+
import { getCollation } from "../../functions/collations.js";
|
|
11
|
+
import { resolveUri } from "../../xpath/eval/uris.js";
|
|
12
|
+
import { required, yesNo } from "./attributes.js";
|
|
13
|
+
import { infoOf } from "./elementInfo.js";
|
|
14
|
+
import { attr, clarkOf, tokens, xsltError } from "../names.js";
|
|
15
|
+
import { compilePattern } from "../patterns/compile.js";
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A name test of xsl:strip-space or xsl:preserve-space: `*`, `prefix:*`,
|
|
19
|
+
* `*:local` or a QName (in the xpath-default-namespace when unprefixed).
|
|
20
|
+
* @param {string} token
|
|
21
|
+
* @param {Element} element
|
|
22
|
+
* @param {object} cx
|
|
23
|
+
* @returns {{uri: string|null, local: string|null, priority: number}}
|
|
24
|
+
*/
|
|
25
|
+
function nameTest(token, element, cx) {
|
|
26
|
+
if (token === "*") return { uri: null, local: null, priority: -0.5 };
|
|
27
|
+
if (token.startsWith("*:")) {
|
|
28
|
+
return { uri: null, local: token.slice(2), priority: -0.25 };
|
|
29
|
+
}
|
|
30
|
+
const eqWildcard = /^Q\{([^}]*)\}\*$/.exec(token);
|
|
31
|
+
if (eqWildcard) return { uri: eqWildcard[1], local: null, priority: -0.25 };
|
|
32
|
+
if (token.endsWith(":*")) {
|
|
33
|
+
const prefix = token.slice(0, -2);
|
|
34
|
+
const uri = infoOf(element).namespaces.get(prefix);
|
|
35
|
+
if (uri === undefined) {
|
|
36
|
+
throw xsltError("XTSE0280", `Undeclared prefix ${prefix}`);
|
|
37
|
+
}
|
|
38
|
+
return { uri, local: null, priority: -0.25 };
|
|
39
|
+
}
|
|
40
|
+
const name = cx.exprs.qname(token, element);
|
|
41
|
+
const uri = name.uri || infoOf(element).xpathDefaultNs;
|
|
42
|
+
return { uri, local: name.local, priority: 0 };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Compiles the whitespace stripping rules.
|
|
47
|
+
* @param {object[]} declarations - xsl:strip-space and xsl:preserve-space
|
|
48
|
+
* @param {object} cx
|
|
49
|
+
* @returns {object[]} rules `{strip, uri, local, priority, precedence,
|
|
50
|
+
* position}` (uri or local null for wildcards), best first
|
|
51
|
+
*/
|
|
52
|
+
export function compileSpaceRules(declarations, cx) {
|
|
53
|
+
const rules = [];
|
|
54
|
+
declarations.forEach(({ element, precedence }, position) => {
|
|
55
|
+
const strip = element.localName === "strip-space";
|
|
56
|
+
for (const token of tokens(required(element, "elements"))) {
|
|
57
|
+
const test = nameTest(token, element, cx);
|
|
58
|
+
rules.push({ strip, ...test, precedence, position });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
const seen = new Map();
|
|
62
|
+
for (const rule of rules) {
|
|
63
|
+
const key = `${rule.precedence}|${rule.uri}|${rule.local}`;
|
|
64
|
+
if (seen.has(key) && seen.get(key) !== rule.strip) {
|
|
65
|
+
throw xsltError(
|
|
66
|
+
"XTSE0270",
|
|
67
|
+
"The same name is in xsl:strip-space and xsl:preserve-space",
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
seen.set(key, rule.strip);
|
|
71
|
+
}
|
|
72
|
+
rules.sort(
|
|
73
|
+
(a, b) =>
|
|
74
|
+
b.precedence - a.precedence ||
|
|
75
|
+
b.priority - a.priority ||
|
|
76
|
+
b.position - a.position,
|
|
77
|
+
);
|
|
78
|
+
return rules;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Collects the namespace aliases.
|
|
83
|
+
* @param {object[]} declarations
|
|
84
|
+
* @returns {Map<string, {uri: string, prefix: string}>} result namespace
|
|
85
|
+
* and prefix by stylesheet namespace URI
|
|
86
|
+
*/
|
|
87
|
+
export function collectAliases(declarations) {
|
|
88
|
+
const aliases = new Map();
|
|
89
|
+
const from = new Map();
|
|
90
|
+
for (const { element, precedence } of declarations) {
|
|
91
|
+
const namespaces = infoOf(element).namespaces;
|
|
92
|
+
const lookup = (name) => {
|
|
93
|
+
const prefix = required(element, name).trim();
|
|
94
|
+
const key = prefix === "#default" ? "" : prefix;
|
|
95
|
+
const uri = namespaces.get(key);
|
|
96
|
+
if (uri === undefined && key !== "") {
|
|
97
|
+
throw xsltError("XTSE0812", `Undeclared prefix ${prefix}`);
|
|
98
|
+
}
|
|
99
|
+
return { prefix: key, uri: uri ?? "" };
|
|
100
|
+
};
|
|
101
|
+
const source = lookup("stylesheet-prefix");
|
|
102
|
+
const result = lookup("result-prefix");
|
|
103
|
+
const previous = aliases.get(source.uri);
|
|
104
|
+
if (
|
|
105
|
+
previous &&
|
|
106
|
+
from.get(source.uri) === precedence &&
|
|
107
|
+
previous.uri !== result.uri
|
|
108
|
+
) {
|
|
109
|
+
throw xsltError("XTSE0810", "Conflicting namespace aliases");
|
|
110
|
+
}
|
|
111
|
+
if (!previous || from.get(source.uri) <= precedence) {
|
|
112
|
+
aliases.set(source.uri, result);
|
|
113
|
+
from.set(source.uri, precedence);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return aliases;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* XTSE1210: the collation of a key must be known.
|
|
121
|
+
* @param {string} uri - The collation attribute
|
|
122
|
+
* @param {Element} element
|
|
123
|
+
*/
|
|
124
|
+
function knownCollation(uri, element) {
|
|
125
|
+
try {
|
|
126
|
+
getCollation(resolveUri(uri.trim(), infoOf(element).baseUri));
|
|
127
|
+
} catch {
|
|
128
|
+
throw xsltError("XTSE1210", `Unknown collation ${uri}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Compiles an xsl:key into the key registry.
|
|
134
|
+
* @param {object} declaration
|
|
135
|
+
* @param {object} cx
|
|
136
|
+
*/
|
|
137
|
+
export function declareKey({ element }, cx) {
|
|
138
|
+
const name = clarkOf(cx.exprs.qname(required(element, "name"), element));
|
|
139
|
+
const use = attr(element, "use");
|
|
140
|
+
const children = cx.children(element);
|
|
141
|
+
if (use !== undefined && children.length > 0) {
|
|
142
|
+
// content that is no sequence constructor is the first error (XTSE0010)
|
|
143
|
+
compileBody(element, cx, cx.globalScope());
|
|
144
|
+
throw xsltError("XTSE1205", "xsl:key cannot have use and content");
|
|
145
|
+
}
|
|
146
|
+
if (use === undefined && children.length === 0) {
|
|
147
|
+
throw xsltError("XTSE1205", "xsl:key needs use or content");
|
|
148
|
+
}
|
|
149
|
+
const scope = cx.globalScope();
|
|
150
|
+
const collation = attr(element, "collation");
|
|
151
|
+
const definition = {
|
|
152
|
+
match: compilePattern(required(element, "match"), element, cx),
|
|
153
|
+
// namespace nodes are indexed only for keys that can match them
|
|
154
|
+
namespaces: required(element, "match").includes("namespace-node"),
|
|
155
|
+
use: use === undefined ? null : cx.exprs.xpath(use, element, scope.vars),
|
|
156
|
+
body: use === undefined ? compileBody(element, cx, scope) : null,
|
|
157
|
+
collation: collation ?? infoOf(element).defaultCollation,
|
|
158
|
+
compatible: infoOf(element).version < 2,
|
|
159
|
+
};
|
|
160
|
+
if (collation !== undefined) knownCollation(collation, element);
|
|
161
|
+
definition.composite = yesNo(element, "composite", false);
|
|
162
|
+
const list = cx.keys.get(name) ?? [];
|
|
163
|
+
if (list.length > 0 && list[0].collation !== definition.collation) {
|
|
164
|
+
throw xsltError("XTSE1220", `The key ${name} has different collations`);
|
|
165
|
+
}
|
|
166
|
+
if (list.length > 0 && list[0].composite !== definition.composite) {
|
|
167
|
+
throw xsltError("XTSE1222", `The key ${name} is composite and not`);
|
|
168
|
+
}
|
|
169
|
+
list.push(definition);
|
|
170
|
+
cx.keys.set(name, list);
|
|
171
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xsl:mode declarations (XSLT 3.0 section 6.7.1): the behaviour of a
|
|
3
|
+
* mode for unmatched and ambiguously matched items, several declarations
|
|
4
|
+
* of a mode combined (XTSE0545 for a conflict at the same import
|
|
5
|
+
* precedence).
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/compiler/modeDecl
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { attr, clarkOf, xsltError } from "../names.js";
|
|
11
|
+
|
|
12
|
+
const ON_NO_MATCH = new Set([
|
|
13
|
+
"deep-copy",
|
|
14
|
+
"shallow-copy",
|
|
15
|
+
"deep-skip",
|
|
16
|
+
"shallow-skip",
|
|
17
|
+
"text-only-copy",
|
|
18
|
+
"fail",
|
|
19
|
+
]);
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Records the attributes of an xsl:mode declaration, noting two values
|
|
23
|
+
* of an attribute at the same import precedence (XTSE0545 unless a
|
|
24
|
+
* declaration of higher precedence settles it).
|
|
25
|
+
* @param {object} mode
|
|
26
|
+
* @param {Element} element
|
|
27
|
+
* @param {number} precedence
|
|
28
|
+
*/
|
|
29
|
+
function recordModeAttributes(mode, element, precedence) {
|
|
30
|
+
mode.declared ??= new Map();
|
|
31
|
+
for (const attribute of element.attributes) {
|
|
32
|
+
const key = attribute.name;
|
|
33
|
+
// use-accumulators is compared as a set (useAccumulators.js)
|
|
34
|
+
if (
|
|
35
|
+
key === "name" ||
|
|
36
|
+
key === "use-accumulators" ||
|
|
37
|
+
key.includes(":") ||
|
|
38
|
+
key.startsWith("xmlns")
|
|
39
|
+
) {
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
const value = attribute.value.trim();
|
|
43
|
+
const previous = mode.declared.get(key);
|
|
44
|
+
const conflict =
|
|
45
|
+
previous?.precedence === precedence &&
|
|
46
|
+
(previous.conflict || previous.value !== value);
|
|
47
|
+
mode.declared.set(key, { value, precedence, conflict });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* XTSE0545: checks the xsl:mode declarations once all are recorded.
|
|
53
|
+
* @param {Iterable<object>} modes
|
|
54
|
+
*/
|
|
55
|
+
export function checkModeDeclarations(modes) {
|
|
56
|
+
for (const mode of modes) {
|
|
57
|
+
for (const [key, { conflict }] of mode.declared ?? []) {
|
|
58
|
+
if (conflict) {
|
|
59
|
+
throw xsltError("XTSE0545", `Conflicting values of ${key} for a mode`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Applies an xsl:mode declaration.
|
|
67
|
+
* @param {object} declaration
|
|
68
|
+
* @param {object} cx
|
|
69
|
+
*/
|
|
70
|
+
export function declareMode({ element, precedence }, cx) {
|
|
71
|
+
if (cx.children(element).length > 0) {
|
|
72
|
+
throw xsltError("XTSE0010", "xsl:mode must be empty");
|
|
73
|
+
}
|
|
74
|
+
const nameText = attr(element, "name");
|
|
75
|
+
const name =
|
|
76
|
+
nameText === undefined ? "" : clarkOf(cx.exprs.qname(nameText, element));
|
|
77
|
+
const mode = cx.mode(name);
|
|
78
|
+
recordModeAttributes(mode, element, precedence);
|
|
79
|
+
const onNoMatch = attr(element, "on-no-match");
|
|
80
|
+
if (onNoMatch !== undefined) {
|
|
81
|
+
if (!ON_NO_MATCH.has(onNoMatch.trim())) {
|
|
82
|
+
throw xsltError("XTSE0020", `Invalid on-no-match ${onNoMatch}`);
|
|
83
|
+
}
|
|
84
|
+
mode.onNoMatch = onNoMatch.trim();
|
|
85
|
+
}
|
|
86
|
+
const onMultiple = attr(element, "on-multiple-match");
|
|
87
|
+
if (onMultiple !== undefined) mode.onMultipleMatch = onMultiple.trim();
|
|
88
|
+
}
|