@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,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* xsl:try and xsl:catch (XSLT 3.0 section 8.3): the result of the try
|
|
3
|
+
* body is built apart, and replaced by the result of the first matching
|
|
4
|
+
* xsl:catch when a dynamic error occurs; the catch body sees the error in
|
|
5
|
+
* the variables err:code, err:description, err:value...
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/xslt/instructions/try
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { AtomicValue } from "../../xdm/atomic.js";
|
|
11
|
+
import { QNameValue } from "../../xdm/qname.js";
|
|
12
|
+
import { types } from "../../xdm/types.js";
|
|
13
|
+
import { compileBody } from "../compiler/body.js";
|
|
14
|
+
import { checkAttributes } from "../compiler/attributes.js";
|
|
15
|
+
import { infoOf } from "../compiler/elementInfo.js";
|
|
16
|
+
import { derive, evaluate } from "../runtime/context.js";
|
|
17
|
+
import { escapesTry, markOutside } from "../runtime/lazy.js";
|
|
18
|
+
import { SequenceReceiver } from "../runtime/sequenceReceiver.js";
|
|
19
|
+
import { attr, isXsl, tokens, xsltError } from "../names.js";
|
|
20
|
+
|
|
21
|
+
/** Namespace of the error codes and of the err:* variables. */
|
|
22
|
+
const ERR = "http://www.w3.org/2005/xqt-errors";
|
|
23
|
+
|
|
24
|
+
/** The variables of a catch body, outermost first. */
|
|
25
|
+
const ERROR_VARIABLES = [
|
|
26
|
+
"code",
|
|
27
|
+
"description",
|
|
28
|
+
"value",
|
|
29
|
+
"module",
|
|
30
|
+
"line-number",
|
|
31
|
+
"column-number",
|
|
32
|
+
"additional",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The expanded name of an error code (`LOCAL` in the err namespace, or
|
|
37
|
+
* `Q{uri}local`).
|
|
38
|
+
* @param {string} code
|
|
39
|
+
* @returns {{uri: string, local: string}}
|
|
40
|
+
*/
|
|
41
|
+
function codeName(code) {
|
|
42
|
+
const eq = /^Q\{([^}]*)\}(.*)$/.exec(code);
|
|
43
|
+
return eq ? { uri: eq[1], local: eq[2] } : { uri: ERR, local: code };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Compiles the name tests of an errors attribute.
|
|
48
|
+
* @param {Element} element - xsl:catch
|
|
49
|
+
* @returns {(name: {uri: string, local: string}) => boolean}
|
|
50
|
+
*/
|
|
51
|
+
function errorTests(element) {
|
|
52
|
+
const namespaces = infoOf(element).namespaces;
|
|
53
|
+
const tests = tokens(attr(element, "errors") ?? "*").map((token) => {
|
|
54
|
+
if (token === "*") return () => true;
|
|
55
|
+
if (token.startsWith("*:")) return (n) => n.local === token.slice(2);
|
|
56
|
+
const eq = /^Q\{([^}]*)\}(.*)$/.exec(token);
|
|
57
|
+
const [uri, local] = eq
|
|
58
|
+
? [eq[1], eq[2]]
|
|
59
|
+
: token.includes(":")
|
|
60
|
+
? [namespaces.get(token.split(":")[0]) ?? "", token.split(":")[1]]
|
|
61
|
+
: ["", token];
|
|
62
|
+
return (n) => n.uri === uri && (local === "*" || n.local === local);
|
|
63
|
+
});
|
|
64
|
+
return (name) => tests.some((test) => test(name));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* An optional atomic value.
|
|
69
|
+
* @param {object} type
|
|
70
|
+
* @param {string|number|undefined} value - Absent when undefined
|
|
71
|
+
* @returns {Array}
|
|
72
|
+
*/
|
|
73
|
+
const optional = (type, value) =>
|
|
74
|
+
value === undefined
|
|
75
|
+
? []
|
|
76
|
+
: [new AtomicValue(type, type === types.integer ? BigInt(value) : value)];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The values of the err:* variables for an error.
|
|
80
|
+
* @param {Error} error
|
|
81
|
+
* @returns {Array[]} in the order of ERROR_VARIABLES
|
|
82
|
+
*/
|
|
83
|
+
function errorValues(error) {
|
|
84
|
+
const { uri, local } = codeName(error.code);
|
|
85
|
+
const description = (error.plainMessage ?? error.message).replace(
|
|
86
|
+
/^[^ ]+: /,
|
|
87
|
+
"",
|
|
88
|
+
);
|
|
89
|
+
return [
|
|
90
|
+
[new AtomicValue(types.QName, new QNameValue(uri, local, "err"))],
|
|
91
|
+
[new AtomicValue(types.string, description)],
|
|
92
|
+
error.errorObject ?? error.value ?? [],
|
|
93
|
+
// where the error was raised (see runtime/locations.js)
|
|
94
|
+
optional(types.string, error.location?.module),
|
|
95
|
+
optional(types.integer, error.location?.line),
|
|
96
|
+
optional(types.integer, error.location?.column),
|
|
97
|
+
[],
|
|
98
|
+
].map((value) => (Array.isArray(value) ? value : [value]));
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Compiles the body or select of xsl:try / xsl:catch.
|
|
103
|
+
* @returns {(xc: object, machine: object) => Array}
|
|
104
|
+
*/
|
|
105
|
+
function compileContent(element, children, cx, scope, conflictCode) {
|
|
106
|
+
const select = attr(element, "select");
|
|
107
|
+
if (select !== undefined && children.length > 0) {
|
|
108
|
+
throw xsltError(
|
|
109
|
+
conflictCode,
|
|
110
|
+
`xsl:${element.localName} has select and content`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
if (select !== undefined) {
|
|
114
|
+
const expr = cx.exprs.xpath(select, element, scope.vars);
|
|
115
|
+
return (xc) => evaluate(expr, xc);
|
|
116
|
+
}
|
|
117
|
+
const body = compileBody(element, cx, scope, children);
|
|
118
|
+
return (xc, machine) => {
|
|
119
|
+
const out = new SequenceReceiver(xc.tx.scratch);
|
|
120
|
+
machine.runBody(body, xc, out);
|
|
121
|
+
return out.items;
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* xsl:try.
|
|
127
|
+
* @param {Element} element
|
|
128
|
+
* @param {object} cx
|
|
129
|
+
* @param {object} scope
|
|
130
|
+
* @returns {Function}
|
|
131
|
+
*/
|
|
132
|
+
export function compileTry(element, cx, scope) {
|
|
133
|
+
const children = cx.children(element).filter((c) => !isXsl(c, "fallback"));
|
|
134
|
+
const first = children.findIndex((child) => isXsl(child, "catch"));
|
|
135
|
+
if (first < 0) throw xsltError("XTSE0010", "xsl:try needs an xsl:catch");
|
|
136
|
+
const catchElements = children.slice(first);
|
|
137
|
+
if (!catchElements.every((child) => isXsl(child, "catch"))) {
|
|
138
|
+
throw xsltError("XTSE0010", "xsl:catch must come last in xsl:try");
|
|
139
|
+
}
|
|
140
|
+
const content = compileContent(
|
|
141
|
+
element,
|
|
142
|
+
children.slice(0, first),
|
|
143
|
+
cx,
|
|
144
|
+
scope,
|
|
145
|
+
"XTSE3140",
|
|
146
|
+
);
|
|
147
|
+
let catchScope = scope;
|
|
148
|
+
for (const name of ERROR_VARIABLES) {
|
|
149
|
+
catchScope = {
|
|
150
|
+
...catchScope,
|
|
151
|
+
vars: { key: `{${ERR}}${name}`, next: catchScope.vars },
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
const catches = catchElements.map((catchElement) => {
|
|
155
|
+
checkAttributes(catchElement);
|
|
156
|
+
return {
|
|
157
|
+
matches: errorTests(catchElement),
|
|
158
|
+
content: compileContent(
|
|
159
|
+
catchElement,
|
|
160
|
+
cx.children(catchElement),
|
|
161
|
+
cx,
|
|
162
|
+
catchScope,
|
|
163
|
+
"XTSE3150",
|
|
164
|
+
),
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
return (xc, out, machine) => {
|
|
168
|
+
let items;
|
|
169
|
+
const marker = {};
|
|
170
|
+
markOutside(xc.env, xc.tx.globalEnv, marker);
|
|
171
|
+
try {
|
|
172
|
+
items = content(xc, machine);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
// errors of variables declared outside are not caught (XSLT 3.0 8.3)
|
|
175
|
+
if (typeof error?.code !== "string" || escapesTry(error, marker)) {
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
const handler = catches.find((c) => c.matches(codeName(error.code)));
|
|
179
|
+
if (!handler) throw error;
|
|
180
|
+
let env = xc.env;
|
|
181
|
+
for (const value of errorValues(error)) env = { value, next: env };
|
|
182
|
+
items = handler.content(derive(xc, { env }), machine);
|
|
183
|
+
}
|
|
184
|
+
for (const item of items) out.item(item);
|
|
185
|
+
};
|
|
186
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Values of variables and parameters (XSLT 3.0 section 9.3): the select
|
|
3
|
+
* expression, or the content as a temporary tree (as a sequence when
|
|
4
|
+
* there is an `as` attribute), converted to the declared type.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/instructions/variables
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { setBaseUri } from "../../functions/baseUris.js";
|
|
10
|
+
import { stringItem } from "../../xpath/eval/atomics.js";
|
|
11
|
+
import { coerce } from "../../xpath/eval/coercion.js";
|
|
12
|
+
import { compileBody } from "../compiler/body.js";
|
|
13
|
+
import { required } from "../compiler/attributes.js";
|
|
14
|
+
import { infoOf } from "../compiler/elementInfo.js";
|
|
15
|
+
import { derive, evaluate } from "../runtime/context.js";
|
|
16
|
+
import { LazyEntry } from "../runtime/lazy.js";
|
|
17
|
+
import { bodySequence, temporaryTree } from "../runtime/values.js";
|
|
18
|
+
import { attr, clarkOf, declaredName, isXsl, xsltError } from "../names.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A converter to a declared sequence type, raising a given error code
|
|
22
|
+
* when the value does not match.
|
|
23
|
+
* @param {object|null} type - Compiled sequence type, null for none
|
|
24
|
+
* @param {string} code - Error code for a type mismatch
|
|
25
|
+
* @param {string} what - Description for messages
|
|
26
|
+
* @param {boolean} [compatible] - XPath 1.0 compatibility
|
|
27
|
+
* @returns {(value: Array) => Array}
|
|
28
|
+
*/
|
|
29
|
+
export function typeConverter(type, code, what, compatible = false) {
|
|
30
|
+
if (!type) return (value) => value;
|
|
31
|
+
return (value) => {
|
|
32
|
+
try {
|
|
33
|
+
return coerce(value, type, { what, compatible });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
if (error.code === "XPTY0004" || error.code === "FORG0001") {
|
|
36
|
+
throw xsltError(code, error.message.replace(/^[A-Z]{4}\d{4}: /, ""));
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Compiles the value of a variable, parameter or with-param.
|
|
45
|
+
* @param {Element} element
|
|
46
|
+
* @param {object} cx
|
|
47
|
+
* @param {object} scope - Scope of the select expression and content
|
|
48
|
+
* @param {string} code - Error code of a type mismatch
|
|
49
|
+
* @returns {{value: (xc: object, machine: object) => Array, type: object|null,
|
|
50
|
+
* hasDefault: boolean}}
|
|
51
|
+
*/
|
|
52
|
+
export function compileValue(element, cx, scope, code) {
|
|
53
|
+
const select = attr(element, "select");
|
|
54
|
+
const asText = attr(element, "as");
|
|
55
|
+
const type =
|
|
56
|
+
asText === undefined ? null : cx.exprs.sequenceType(asText, element);
|
|
57
|
+
const children = cx
|
|
58
|
+
.children(element)
|
|
59
|
+
.filter((child) => !isXsl(child, "fallback"));
|
|
60
|
+
if (select !== undefined && children.length > 0) {
|
|
61
|
+
throw xsltError(
|
|
62
|
+
"XTSE0620",
|
|
63
|
+
`xsl:${element.localName} cannot have both select and content`,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
const name = attr(element, "name") ?? "";
|
|
67
|
+
const convert = typeConverter(
|
|
68
|
+
type,
|
|
69
|
+
code,
|
|
70
|
+
`value of $${name}`,
|
|
71
|
+
infoOf(element).version < 2,
|
|
72
|
+
);
|
|
73
|
+
const hasDefault = select !== undefined || children.length > 0;
|
|
74
|
+
if (select !== undefined) {
|
|
75
|
+
const expr = cx.exprs.xpath(select, element, scope.vars);
|
|
76
|
+
return { value: (xc) => convert(evaluate(expr, xc)), type, hasDefault };
|
|
77
|
+
}
|
|
78
|
+
if (children.length > 0) {
|
|
79
|
+
const body = compileBody(element, cx, scope, children);
|
|
80
|
+
const base = infoOf(element).baseUri;
|
|
81
|
+
const value = type
|
|
82
|
+
? (xc, machine) => convert(bodySequence(body, xc, machine))
|
|
83
|
+
: (xc, machine) => [setBaseUri(temporaryTree(body, xc, machine), base)];
|
|
84
|
+
return { value, type, hasDefault };
|
|
85
|
+
}
|
|
86
|
+
const empty = type ? [] : [stringItem("")];
|
|
87
|
+
return { value: () => convert(empty), type, hasDefault };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The Clark name of a variable or parameter.
|
|
92
|
+
* @param {Element} element
|
|
93
|
+
* @param {object} cx
|
|
94
|
+
* @returns {string}
|
|
95
|
+
*/
|
|
96
|
+
export const variableName = (element, cx) =>
|
|
97
|
+
clarkOf(declaredName(cx.exprs.qname(required(element, "name"), element)));
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Compiles a local xsl:variable.
|
|
101
|
+
* @param {Element} element
|
|
102
|
+
* @param {object} cx
|
|
103
|
+
* @param {object} scope
|
|
104
|
+
* @returns {{step: {binding: Function}, scope: object}} the binding step and
|
|
105
|
+
* the scope of the following siblings
|
|
106
|
+
*/
|
|
107
|
+
export function compileLocalVariable(element, cx, scope) {
|
|
108
|
+
const key = variableName(element, cx);
|
|
109
|
+
const { value } = compileValue(element, cx, scope, "XTTE0570");
|
|
110
|
+
return {
|
|
111
|
+
step: {
|
|
112
|
+
binding: (xc, machine) =>
|
|
113
|
+
derive(xc, {
|
|
114
|
+
env: new LazyEntry(xc.env, () => value(xc, machine), key),
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
scope: { ...scope, vars: { key, next: scope.vars } },
|
|
118
|
+
};
|
|
119
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names shared by the XSLT compiler and runtime: namespace URIs, the
|
|
3
|
+
* errors of the XSLT specification, attribute access and the resolution
|
|
4
|
+
* of the QNames written in stylesheet attributes.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/xslt/names
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { XPathError } from "../errors.js";
|
|
10
|
+
|
|
11
|
+
/** The XSLT namespace. */
|
|
12
|
+
export const XSL_NS = "http://www.w3.org/1999/XSL/Transform";
|
|
13
|
+
/** Namespace of namespace declarations. */
|
|
14
|
+
export const XMLNS_NS = "http://www.w3.org/2000/xmlns/";
|
|
15
|
+
/** The namespace bound to the xml prefix. */
|
|
16
|
+
export const XML_NS = "http://www.w3.org/XML/1998/namespace";
|
|
17
|
+
|
|
18
|
+
// combining marks (U+0300-036F) are in \p{M}
|
|
19
|
+
const NCNAME = /^[\p{L}_][\p{L}\p{N}\p{M}_.\-·‿⁀]*$/u;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @param {string} text
|
|
23
|
+
* @returns {boolean} whether the text is an NCName
|
|
24
|
+
*/
|
|
25
|
+
export const isNCName = (text) => NCNAME.test(text);
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {string} text
|
|
29
|
+
* @returns {boolean} whether the text is a lexical QName
|
|
30
|
+
*/
|
|
31
|
+
export function isQName(text) {
|
|
32
|
+
const colon = text.indexOf(":");
|
|
33
|
+
if (colon < 0) return isNCName(text);
|
|
34
|
+
return isNCName(text.slice(0, colon)) && isNCName(text.slice(colon + 1));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* An error of the XSLT specification.
|
|
39
|
+
* @param {string} code - Error code, e.g. "XTSE0010"
|
|
40
|
+
* @param {string} message
|
|
41
|
+
* @returns {XPathError}
|
|
42
|
+
*/
|
|
43
|
+
export const xsltError = (code, message) => new XPathError(code, message);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param {Node} node
|
|
47
|
+
* @param {string} [local] - Local name required
|
|
48
|
+
* @returns {boolean} whether the node is an element of the XSLT namespace
|
|
49
|
+
*/
|
|
50
|
+
export const isXsl = (node, local) =>
|
|
51
|
+
node.nodeType === 1 &&
|
|
52
|
+
node.namespaceURI === XSL_NS &&
|
|
53
|
+
(local === undefined || node.localName === local);
|
|
54
|
+
|
|
55
|
+
/** @type {WeakMap<Element, Map<string, string>>} shadow attribute values */
|
|
56
|
+
const shadows = new WeakMap();
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Records the values of the shadow attributes of an element (XSLT 3.0
|
|
60
|
+
* section 3.13.2): they replace the attributes of the same name.
|
|
61
|
+
* @param {Element} element
|
|
62
|
+
* @param {Map<string, string>} values - By attribute name
|
|
63
|
+
*/
|
|
64
|
+
export function setShadowAttributes(element, values) {
|
|
65
|
+
shadows.set(element, values);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Value of an attribute without namespace (or of its shadow attribute).
|
|
70
|
+
* @param {Element} element
|
|
71
|
+
* @param {string} name
|
|
72
|
+
* @returns {string|undefined} undefined when absent
|
|
73
|
+
*/
|
|
74
|
+
export function attr(element, name) {
|
|
75
|
+
const shadow = shadows.get(element)?.get(name);
|
|
76
|
+
if (shadow !== undefined) return shadow;
|
|
77
|
+
const node = element.getAttributeNode(name);
|
|
78
|
+
return node && !node.namespaceURI ? node.value : undefined;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Value of an attribute in the XSLT namespace (standard attributes of
|
|
83
|
+
* literal result elements, such as xsl:version).
|
|
84
|
+
* @param {Element} element
|
|
85
|
+
* @param {string} local
|
|
86
|
+
* @returns {string|undefined}
|
|
87
|
+
*/
|
|
88
|
+
export function xslAttr(element, local) {
|
|
89
|
+
return element.hasAttributeNS(XSL_NS, local)
|
|
90
|
+
? element.getAttributeNS(XSL_NS, local)
|
|
91
|
+
: undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* A standard attribute: unprefixed on XSLT elements, in the XSLT
|
|
96
|
+
* namespace on other elements.
|
|
97
|
+
* @param {Element} element
|
|
98
|
+
* @param {string} local
|
|
99
|
+
* @returns {string|undefined}
|
|
100
|
+
*/
|
|
101
|
+
export const standardAttr = (element, local) =>
|
|
102
|
+
element.namespaceURI === XSL_NS
|
|
103
|
+
? attr(element, local)
|
|
104
|
+
: xslAttr(element, local);
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* @typedef {object} ExpandedName
|
|
108
|
+
* @property {string} uri - Namespace URI, "" for none
|
|
109
|
+
* @property {string} local - Local part
|
|
110
|
+
* @property {string} prefix - Prefix as written, "" for none
|
|
111
|
+
*/
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Resolves a QName or EQName written in a stylesheet.
|
|
115
|
+
* @param {string} text
|
|
116
|
+
* @param {Map<string, string>} namespaces - In-scope namespaces
|
|
117
|
+
* @param {object} [options]
|
|
118
|
+
* @param {boolean} [options.useDefault] - Unprefixed names take the
|
|
119
|
+
* default namespace
|
|
120
|
+
* @param {string} [options.code] - Error code of an undeclared prefix
|
|
121
|
+
* (default XTSE0280), and of an invalid name when `syntaxCode` is absent
|
|
122
|
+
* @param {string} [options.syntaxCode] - Error code of an invalid name
|
|
123
|
+
* (default XTSE0020 with the default `code`)
|
|
124
|
+
* @returns {ExpandedName}
|
|
125
|
+
* @throws {XPathError} for an invalid name or undeclared prefix
|
|
126
|
+
*/
|
|
127
|
+
export function resolveQName(text, namespaces, options = {}) {
|
|
128
|
+
const { useDefault = false, code = "XTSE0280" } = options;
|
|
129
|
+
const syntaxCode =
|
|
130
|
+
options.syntaxCode ?? (options.code === undefined ? "XTSE0020" : code);
|
|
131
|
+
const name = text.trim();
|
|
132
|
+
const eq = /^Q\{([^{}]*)\}(.*)$/s.exec(name);
|
|
133
|
+
if (eq) {
|
|
134
|
+
if (!isNCName(eq[2])) throw xsltError(syntaxCode, `Invalid name ${name}`);
|
|
135
|
+
return { uri: eq[1], local: eq[2], prefix: "" };
|
|
136
|
+
}
|
|
137
|
+
if (!isQName(name)) throw xsltError(syntaxCode, `Invalid QName "${name}"`);
|
|
138
|
+
const colon = name.indexOf(":");
|
|
139
|
+
if (colon < 0) {
|
|
140
|
+
const uri = useDefault ? (namespaces.get("") ?? "") : "";
|
|
141
|
+
return { uri, local: name, prefix: "" };
|
|
142
|
+
}
|
|
143
|
+
const prefix = name.slice(0, colon);
|
|
144
|
+
const uri = namespaces.get(prefix);
|
|
145
|
+
if (uri === undefined || uri === "") {
|
|
146
|
+
throw xsltError(code, `The prefix ${prefix} is not declared`);
|
|
147
|
+
}
|
|
148
|
+
return { uri, local: name.slice(colon + 1), prefix };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** Namespaces in which stylesheets cannot declare names (XTSE0080). */
|
|
152
|
+
export const RESERVED_NAMESPACES = new Set([
|
|
153
|
+
XSL_NS,
|
|
154
|
+
"http://www.w3.org/2005/xpath-functions",
|
|
155
|
+
"http://www.w3.org/2005/xpath-functions/math",
|
|
156
|
+
"http://www.w3.org/2005/xpath-functions/map",
|
|
157
|
+
"http://www.w3.org/2005/xpath-functions/array",
|
|
158
|
+
"http://www.w3.org/2001/XMLSchema",
|
|
159
|
+
"http://www.w3.org/2001/XMLSchema-instance",
|
|
160
|
+
XML_NS,
|
|
161
|
+
]);
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Checks that a declared name is not in a reserved namespace.
|
|
165
|
+
* @param {ExpandedName} name
|
|
166
|
+
* @param {string} [allowed] - Clark name allowed anyway
|
|
167
|
+
* (`{XSL}initial-template` for templates)
|
|
168
|
+
* @returns {ExpandedName} the name
|
|
169
|
+
* @throws {XPathError} XTSE0080
|
|
170
|
+
*/
|
|
171
|
+
export function declaredName(name, allowed) {
|
|
172
|
+
if (RESERVED_NAMESPACES.has(name.uri) && clarkOf(name) !== allowed) {
|
|
173
|
+
throw xsltError("XTSE0080", `The namespace ${name.uri} is reserved`);
|
|
174
|
+
}
|
|
175
|
+
return name;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* @param {{uri: string, local: string}} name
|
|
180
|
+
* @returns {string} Clark notation `{uri}local`
|
|
181
|
+
*/
|
|
182
|
+
export const clarkOf = (name) => `{${name.uri}}${name.local}`;
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* A Clark name as messages show it: `local` without namespace, else the
|
|
186
|
+
* EQName `Q{uri}local`.
|
|
187
|
+
* @param {string} clark - `{uri}local` (other names are kept)
|
|
188
|
+
* @returns {string}
|
|
189
|
+
*/
|
|
190
|
+
export function displayName(clark) {
|
|
191
|
+
if (clark.startsWith("{}")) return clark.slice(2);
|
|
192
|
+
return clark.startsWith("{") ? `Q${clark}` : clark;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Splits a whitespace-separated list.
|
|
197
|
+
* @param {string|undefined} text
|
|
198
|
+
* @returns {string[]}
|
|
199
|
+
*/
|
|
200
|
+
export const tokens = (text) => (text ?? "").split(/\s+/).filter(Boolean);
|
|
201
|
+
|
|
202
|
+
/** The name xsl:original, by which an overriding component reaches the
|
|
203
|
+
* overridden one (XSLT 3.0 section 3.5.3.4), in Clark notation. */
|
|
204
|
+
export const ORIGINAL = `{${XSL_NS}}original`;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The origins of an axis step in a pattern (see general.js): the nodes
|
|
3
|
+
* from which the step selects a node, found through the inverse of its
|
|
4
|
+
* axis, with the child-or-top, attribute-or-top and namespace-or-top
|
|
5
|
+
* adjustments of the first step of a relative pattern (XSLT 3.0 section
|
|
6
|
+
* 5.5.3).
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/xslt/patterns/axisOrigins
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { parentOf } from "../../xpath/eval/domNodes.js";
|
|
12
|
+
import { compileNode } from "../../xpath/eval/compiler.js";
|
|
13
|
+
import { compileNodeTest } from "../../xpath/eval/nodeTests.js";
|
|
14
|
+
import { compilePredicate } from "../../xpath/eval/predicates.js";
|
|
15
|
+
|
|
16
|
+
/** Node kinds the child-or-top axis selects when they have no parent. */
|
|
17
|
+
const TOP_KINDS = new Set([1, 3, 4, 7, 8]);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The ancestors of a node, nearest first.
|
|
21
|
+
* @param {Node} node
|
|
22
|
+
* @returns {Node[]}
|
|
23
|
+
*/
|
|
24
|
+
function ancestors(node) {
|
|
25
|
+
const result = [];
|
|
26
|
+
for (let a = parentOf(node); a; a = parentOf(a)) result.push(a);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The nodes from which an axis can reach a node.
|
|
32
|
+
* @param {string} axis
|
|
33
|
+
* @param {Node} node
|
|
34
|
+
* @returns {Node[]}
|
|
35
|
+
*/
|
|
36
|
+
function inverseAxis(axis, node) {
|
|
37
|
+
switch (axis) {
|
|
38
|
+
case "self":
|
|
39
|
+
return [node];
|
|
40
|
+
case "descendant":
|
|
41
|
+
return ancestors(node);
|
|
42
|
+
case "descendant-or-self":
|
|
43
|
+
return [node, ...ancestors(node)];
|
|
44
|
+
default: {
|
|
45
|
+
const parent = parentOf(node);
|
|
46
|
+
return parent ? [parent] : [];
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Whether a step selects nodes as child-or-top / attribute-or-top /
|
|
53
|
+
* namespace-or-top does for a parentless node (the first step of a
|
|
54
|
+
* relative pattern).
|
|
55
|
+
* @param {object} step - AxisStep
|
|
56
|
+
* @param {Node} node
|
|
57
|
+
* @returns {boolean}
|
|
58
|
+
*/
|
|
59
|
+
function selectsTop(step, node) {
|
|
60
|
+
if (parentOf(node)) return false;
|
|
61
|
+
if (step.axis === "child") return TOP_KINDS.has(node.nodeType);
|
|
62
|
+
return step.axis === "attribute" || step.axis === "namespace";
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Compiles predicates applied to a node alone (position 1 of 1).
|
|
67
|
+
* @param {object[]} predicates
|
|
68
|
+
* @param {object} env
|
|
69
|
+
* @returns {(node: Node, xc: object) => Node[]} [node] when it passes
|
|
70
|
+
*/
|
|
71
|
+
export function filterAlone(predicates, env) {
|
|
72
|
+
const scope = { sc: env.sc, vars: env.vars };
|
|
73
|
+
const compiled = predicates.map((p) =>
|
|
74
|
+
compilePredicate(p, scope, compileNode),
|
|
75
|
+
);
|
|
76
|
+
return (node, xc) => {
|
|
77
|
+
const ctx = env.cx.patternContext(node, xc, env.local);
|
|
78
|
+
let items = [node];
|
|
79
|
+
for (const predicate of compiled) items = predicate(items, ctx);
|
|
80
|
+
return items;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Compiles an axis step: `origins(node, xc)` returns the context nodes
|
|
86
|
+
* from which the step selects the node.
|
|
87
|
+
* @param {object} step - AxisStep
|
|
88
|
+
* @param {object} env - See compile.js
|
|
89
|
+
* @param {boolean} first - First step of a relative pattern
|
|
90
|
+
* @param {(ctx: object) => Array} run - The step compiled as an expression
|
|
91
|
+
* @returns {{origins: (node: Node, xc: object) => Node[]}}
|
|
92
|
+
*/
|
|
93
|
+
export function compileAxisOrigins(step, env, first, run) {
|
|
94
|
+
// document-node() without an axis matches document nodes (self axis)
|
|
95
|
+
const documentStep =
|
|
96
|
+
first &&
|
|
97
|
+
step.axis === "child" &&
|
|
98
|
+
step.nodeTest.type === "DocumentTest" &&
|
|
99
|
+
!env.text.startsWith("child::", step.start);
|
|
100
|
+
const axis = documentStep ? "self" : step.axis;
|
|
101
|
+
const effective = documentStep ? { ...step, axis } : step;
|
|
102
|
+
const evaluate = documentStep
|
|
103
|
+
? env.cx.exprs.compileAst(effective, env.sc, env.vars)
|
|
104
|
+
: run;
|
|
105
|
+
const nodeTest = compileNodeTest(step.nodeTest, axis, env.sc);
|
|
106
|
+
const simple = step.predicates.length === 0;
|
|
107
|
+
const alone = filterAlone(step.predicates, env);
|
|
108
|
+
return {
|
|
109
|
+
origins(node, xc) {
|
|
110
|
+
if (first && !documentStep && selectsTop(step, node)) {
|
|
111
|
+
// the predicates see the node alone (position 1 of 1)
|
|
112
|
+
return nodeTest(node) ? alone(node, xc) : [];
|
|
113
|
+
}
|
|
114
|
+
const candidates = inverseAxis(axis, node);
|
|
115
|
+
if (simple) return nodeTest(node) ? candidates : [];
|
|
116
|
+
return candidates.filter((c) =>
|
|
117
|
+
evaluate(env.cx.patternContext(c, xc, env.local)).includes(node),
|
|
118
|
+
);
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|