@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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions on nodes (F&O 3.1 section 13): names, root, children,
|
|
3
|
+
* innermost/outermost, generate-id and lang. Functions with an optional
|
|
4
|
+
* node argument use the context item when it is omitted.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/functions/nodes
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { AtomicValue } from "../xdm/atomic.js";
|
|
10
|
+
import { QNameValue } from "../xdm/qname.js";
|
|
11
|
+
import { types } from "../xdm/types.js";
|
|
12
|
+
import { booleanItem, stringItem } from "../xpath/eval/atomics.js";
|
|
13
|
+
import {
|
|
14
|
+
childrenOf,
|
|
15
|
+
nodeLocalName,
|
|
16
|
+
nodeNamespace,
|
|
17
|
+
nodePrefix,
|
|
18
|
+
parentOf,
|
|
19
|
+
rootOf,
|
|
20
|
+
} from "../xpath/eval/domNodes.js";
|
|
21
|
+
import { focusNode } from "./focus.js";
|
|
22
|
+
import { nodePathFunctions } from "./nodePaths.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Definitions of a function of an optional node, with the context node as
|
|
26
|
+
* default (arity 0 and 1).
|
|
27
|
+
* @param {string} local
|
|
28
|
+
* @param {string} returns
|
|
29
|
+
* @param {(node: Node|undefined, context: object) => Array} impl - Called
|
|
30
|
+
* with undefined for an empty argument
|
|
31
|
+
* @returns {object[]}
|
|
32
|
+
*/
|
|
33
|
+
export function nodeFunction(local, returns, impl) {
|
|
34
|
+
return [
|
|
35
|
+
{
|
|
36
|
+
local,
|
|
37
|
+
params: [],
|
|
38
|
+
returns,
|
|
39
|
+
focus: true,
|
|
40
|
+
impl: (_, context) => impl(focusNode(context), context),
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
local,
|
|
44
|
+
params: ["node()?"],
|
|
45
|
+
returns,
|
|
46
|
+
impl: ([arg], context) => impl(arg[0], context),
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** @param {Node} node @returns {boolean} whether the node kind has a name */
|
|
52
|
+
const isNamed = (node) =>
|
|
53
|
+
[1, 2, 7].includes(node.nodeType) ||
|
|
54
|
+
(node.nodeType === 13 && node.localName !== "");
|
|
55
|
+
|
|
56
|
+
/** @param {Node} node @returns {string} the lexical QName of a node */
|
|
57
|
+
function lexicalName(node) {
|
|
58
|
+
if (!isNamed(node)) return "";
|
|
59
|
+
const prefix = nodePrefix(node);
|
|
60
|
+
const local = nodeLocalName(node);
|
|
61
|
+
return prefix ? `${prefix}:${local}` : local;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @type {WeakMap<object, string>} */
|
|
65
|
+
const ids = new WeakMap();
|
|
66
|
+
let nextId = 0;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {Node} node
|
|
70
|
+
* @returns {string} an identifier unique to the node
|
|
71
|
+
*/
|
|
72
|
+
function generateId(node) {
|
|
73
|
+
let id = ids.get(node);
|
|
74
|
+
if (id === undefined) {
|
|
75
|
+
id = `n${nextId++}`;
|
|
76
|
+
ids.set(node, id);
|
|
77
|
+
}
|
|
78
|
+
return id;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* fn:lang: whether the xml:lang of a node matches a language.
|
|
83
|
+
* @param {string} language
|
|
84
|
+
* @param {Node} node
|
|
85
|
+
* @returns {boolean}
|
|
86
|
+
*/
|
|
87
|
+
function matchesLanguage(language, node) {
|
|
88
|
+
const xml = "http://www.w3.org/XML/1998/namespace";
|
|
89
|
+
for (let n = node; n; n = parentOf(n)) {
|
|
90
|
+
const value = n.nodeType === 1 ? n.getAttributeNS(xml, "lang") : null;
|
|
91
|
+
if (value !== null && n.hasAttributeNS(xml, "lang")) {
|
|
92
|
+
const actual = value.toLowerCase();
|
|
93
|
+
const wanted = language.toLowerCase();
|
|
94
|
+
return actual === wanted || actual.startsWith(`${wanted}-`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The topmost or bottommost nodes of a set.
|
|
102
|
+
* @param {Node[]} nodes
|
|
103
|
+
* @param {object} context
|
|
104
|
+
* @param {boolean} inner - innermost (else outermost)
|
|
105
|
+
* @returns {Node[]}
|
|
106
|
+
*/
|
|
107
|
+
function nesting(nodes, context, inner) {
|
|
108
|
+
const set = new Set(nodes);
|
|
109
|
+
const hasAncestorIn = (node) => {
|
|
110
|
+
for (let p = parentOf(node); p; p = parentOf(p)) {
|
|
111
|
+
if (set.has(p)) return true;
|
|
112
|
+
}
|
|
113
|
+
return false;
|
|
114
|
+
};
|
|
115
|
+
const ancestors = new Set();
|
|
116
|
+
if (inner) {
|
|
117
|
+
for (const node of nodes) {
|
|
118
|
+
for (let p = parentOf(node); p; p = parentOf(p)) ancestors.add(p);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const kept = nodes.filter((node) =>
|
|
122
|
+
inner ? !ancestors.has(node) : !hasAncestorIn(node),
|
|
123
|
+
);
|
|
124
|
+
return context.order.sort(kept);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/** Function definitions. */
|
|
128
|
+
export const nodeFunctions = [
|
|
129
|
+
...nodeFunction("name", "xs:string", (node) => [
|
|
130
|
+
stringItem(node ? lexicalName(node) : ""),
|
|
131
|
+
]),
|
|
132
|
+
...nodeFunction("local-name", "xs:string", (node) => [
|
|
133
|
+
stringItem(node && isNamed(node) ? nodeLocalName(node) : ""),
|
|
134
|
+
]),
|
|
135
|
+
...nodeFunction("namespace-uri", "xs:anyURI", (node) => [
|
|
136
|
+
new AtomicValue(
|
|
137
|
+
types.anyURI,
|
|
138
|
+
node && (node.nodeType === 1 || node.nodeType === 2)
|
|
139
|
+
? nodeNamespace(node)
|
|
140
|
+
: "",
|
|
141
|
+
),
|
|
142
|
+
]),
|
|
143
|
+
...nodeFunction("node-name", "xs:QName?", (node) =>
|
|
144
|
+
node && isNamed(node)
|
|
145
|
+
? [
|
|
146
|
+
new AtomicValue(
|
|
147
|
+
types.QName,
|
|
148
|
+
new QNameValue(
|
|
149
|
+
nodeNamespace(node),
|
|
150
|
+
nodeLocalName(node),
|
|
151
|
+
nodePrefix(node),
|
|
152
|
+
),
|
|
153
|
+
),
|
|
154
|
+
]
|
|
155
|
+
: [],
|
|
156
|
+
),
|
|
157
|
+
...nodeFunction("root", "node()?", (node) => (node ? [rootOf(node)] : [])),
|
|
158
|
+
...nodeFunction("has-children", "xs:boolean", (node) => [
|
|
159
|
+
booleanItem(Boolean(node) && childrenOf(node).length > 0),
|
|
160
|
+
]),
|
|
161
|
+
...nodeFunction("nilled", "xs:boolean?", (node) =>
|
|
162
|
+
node?.nodeType === 1 ? [booleanItem(false)] : [],
|
|
163
|
+
),
|
|
164
|
+
...nodeFunction("generate-id", "xs:string", (node) => [
|
|
165
|
+
stringItem(node ? generateId(node) : ""),
|
|
166
|
+
]),
|
|
167
|
+
{
|
|
168
|
+
local: "innermost",
|
|
169
|
+
params: ["node()*"],
|
|
170
|
+
returns: "node()*",
|
|
171
|
+
impl: ([nodes], context) => nesting(nodes, context, true),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
local: "outermost",
|
|
175
|
+
params: ["node()*"],
|
|
176
|
+
returns: "node()*",
|
|
177
|
+
impl: ([nodes], context) => nesting(nodes, context, false),
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
local: "lang",
|
|
181
|
+
params: ["xs:string?"],
|
|
182
|
+
returns: "xs:boolean",
|
|
183
|
+
focus: true,
|
|
184
|
+
impl: ([language], context) => [
|
|
185
|
+
booleanItem(
|
|
186
|
+
matchesLanguage(language[0]?.value ?? "", focusNode(context)),
|
|
187
|
+
),
|
|
188
|
+
],
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
local: "lang",
|
|
192
|
+
params: ["xs:string?", "node()"],
|
|
193
|
+
returns: "xs:boolean",
|
|
194
|
+
impl: ([language, [node]]) => [
|
|
195
|
+
booleanItem(matchesLanguage(language[0]?.value ?? "", node)),
|
|
196
|
+
],
|
|
197
|
+
},
|
|
198
|
+
...nodePathFunctions,
|
|
199
|
+
];
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions on numeric values (F&O 3.1 section 4.4): abs, ceiling, floor,
|
|
3
|
+
* round and round-half-to-even. Results have the primitive numeric type of
|
|
4
|
+
* the argument (xs:integer for integer subtypes). Decimal and integer
|
|
5
|
+
* rounding is exact; doubles (and floats, widened to doubles) are rounded
|
|
6
|
+
* through the shortest decimal that reads back as the double, so
|
|
7
|
+
* round(0.49999999999999994) is 0.
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/functions/numeric
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { AtomicValue } from "../xdm/atomic.js";
|
|
13
|
+
import { Decimal } from "../xdm/decimal.js";
|
|
14
|
+
import { numberToDecimal } from "../xdm/numeric.js";
|
|
15
|
+
import { types } from "../xdm/types.js";
|
|
16
|
+
import { define } from "./support.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Rounds a decimal to `precision` fraction digits (negative: to a power of
|
|
20
|
+
* ten).
|
|
21
|
+
* @param {Decimal} value
|
|
22
|
+
* @param {number} precision
|
|
23
|
+
* @param {boolean} halfEven - Ties to even; otherwise toward +INF
|
|
24
|
+
* @returns {Decimal}
|
|
25
|
+
*/
|
|
26
|
+
export function roundDecimal(value, precision, halfEven) {
|
|
27
|
+
if (value.scale <= precision) return value;
|
|
28
|
+
const divisor = 10n ** BigInt(value.scale - precision);
|
|
29
|
+
let quotient = value.unscaled / divisor;
|
|
30
|
+
let remainder = value.unscaled % divisor;
|
|
31
|
+
if (remainder < 0n) {
|
|
32
|
+
quotient -= 1n;
|
|
33
|
+
remainder += divisor;
|
|
34
|
+
}
|
|
35
|
+
const twice = 2n * remainder;
|
|
36
|
+
const up =
|
|
37
|
+
twice > divisor ||
|
|
38
|
+
(twice === divisor && (!halfEven || quotient % 2n !== 0n));
|
|
39
|
+
return Decimal.of(up ? quotient + 1n : quotient, precision);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* @param {AtomicValue} item
|
|
44
|
+
* @param {*} value
|
|
45
|
+
* @returns {AtomicValue} the value with the item's primitive numeric type
|
|
46
|
+
*/
|
|
47
|
+
function sameType(item, value) {
|
|
48
|
+
const type = typeof value === "bigint" ? types.integer : item.type.primitive;
|
|
49
|
+
return new AtomicValue(type, value);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Rounds a numeric item.
|
|
54
|
+
* @param {AtomicValue} item
|
|
55
|
+
* @param {number} precision - Already clamped to a safe range
|
|
56
|
+
* @param {boolean} halfEven
|
|
57
|
+
* @returns {AtomicValue}
|
|
58
|
+
*/
|
|
59
|
+
function roundItem(item, precision, halfEven) {
|
|
60
|
+
const { value } = item;
|
|
61
|
+
if (typeof value === "bigint") {
|
|
62
|
+
if (precision >= 0) return sameType(item, value);
|
|
63
|
+
return sameType(
|
|
64
|
+
item,
|
|
65
|
+
roundDecimal(Decimal.of(value), precision, halfEven).unscaled,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (value instanceof Decimal) {
|
|
69
|
+
return sameType(item, roundDecimal(value, precision, halfEven));
|
|
70
|
+
}
|
|
71
|
+
if (!Number.isFinite(value) || value === 0) return sameType(item, value);
|
|
72
|
+
const isFloat = item.type.primitive === types.float;
|
|
73
|
+
// floats through their double value: xs:float("0.05") is above 0.05
|
|
74
|
+
const exact = numberToDecimal(value);
|
|
75
|
+
let rounded = Number(roundDecimal(exact, precision, halfEven).toString());
|
|
76
|
+
if (rounded === 0 && value < 0) rounded = -0;
|
|
77
|
+
return sameType(item, isFloat ? Math.fround(rounded) : rounded);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Largest precision that can change a double or decimal in practice. */
|
|
81
|
+
const PRECISION_LIMIT = 10000;
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {Array<*>[]} args - [$arg, $precision?]
|
|
85
|
+
* @param {boolean} halfEven
|
|
86
|
+
* @returns {Array<*>}
|
|
87
|
+
*/
|
|
88
|
+
function roundFunction([arg, precision], halfEven) {
|
|
89
|
+
if (arg.length === 0) return [];
|
|
90
|
+
const p = precision === undefined ? 0n : precision[0].value;
|
|
91
|
+
const clamped =
|
|
92
|
+
p > BigInt(PRECISION_LIMIT)
|
|
93
|
+
? PRECISION_LIMIT
|
|
94
|
+
: p < BigInt(-PRECISION_LIMIT)
|
|
95
|
+
? -PRECISION_LIMIT
|
|
96
|
+
: Number(p);
|
|
97
|
+
return [roundItem(arg[0], clamped, halfEven)];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Declares a one-argument numeric function.
|
|
102
|
+
* @param {string} local
|
|
103
|
+
* @param {(value: *, item: AtomicValue) => *} apply - On the value
|
|
104
|
+
* @returns {import("./support.js").FunctionDefinition}
|
|
105
|
+
*/
|
|
106
|
+
const unary = (local, apply) =>
|
|
107
|
+
define(local, ["xs:numeric?"], "xs:numeric?", ([arg]) =>
|
|
108
|
+
arg.length === 0 ? [] : [sameType(arg[0], apply(arg[0].value))],
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
/** @param {*} v @returns {*} the largest integer not above v */
|
|
112
|
+
const floor = (v) =>
|
|
113
|
+
typeof v === "bigint"
|
|
114
|
+
? v
|
|
115
|
+
: v instanceof Decimal
|
|
116
|
+
? Decimal.of(v.floor())
|
|
117
|
+
: Math.floor(v);
|
|
118
|
+
|
|
119
|
+
const N = "xs:numeric?";
|
|
120
|
+
|
|
121
|
+
/** @type {import("./support.js").FunctionDefinition[]} */
|
|
122
|
+
export const numericFunctions = [
|
|
123
|
+
unary("abs", (v) =>
|
|
124
|
+
typeof v === "bigint"
|
|
125
|
+
? v < 0n
|
|
126
|
+
? -v
|
|
127
|
+
: v
|
|
128
|
+
: v instanceof Decimal
|
|
129
|
+
? v.sign() < 0
|
|
130
|
+
? v.neg()
|
|
131
|
+
: v
|
|
132
|
+
: Math.abs(v),
|
|
133
|
+
),
|
|
134
|
+
unary("floor", floor),
|
|
135
|
+
unary("ceiling", (v) =>
|
|
136
|
+
typeof v === "bigint"
|
|
137
|
+
? v
|
|
138
|
+
: v instanceof Decimal
|
|
139
|
+
? floor(v.neg()).neg()
|
|
140
|
+
: Math.ceil(v),
|
|
141
|
+
),
|
|
142
|
+
define("round", [N], N, (args) => roundFunction(args, false)),
|
|
143
|
+
define("round", [N, "xs:integer"], N, (args) => roundFunction(args, false)),
|
|
144
|
+
define("round-half-to-even", [N], N, (args) => roundFunction(args, true)),
|
|
145
|
+
define("round-half-to-even", [N, "xs:integer"], N, (args) =>
|
|
146
|
+
roundFunction(args, true),
|
|
147
|
+
),
|
|
148
|
+
];
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Option maps (F&O 3.1 section 1.5, "Options"): the `$options` argument
|
|
3
|
+
* of fn:parse-json, fn:json-to-xml, fn:xml-to-json... Each known option
|
|
4
|
+
* is converted to its declared type with the function conversion rules
|
|
5
|
+
* (XPTY0004 when it cannot be), checked against its allowed values, and
|
|
6
|
+
* defaulted when absent; unknown options are ignored.
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/functions/options
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { XPathError } from "../errors.js";
|
|
12
|
+
import { stringItem } from "../xpath/eval/atomics.js";
|
|
13
|
+
import { coerce } from "../xpath/eval/coercion.js";
|
|
14
|
+
import { sequenceTypeOf } from "../xpath/eval/functionItems.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Declaration of one option.
|
|
18
|
+
* @typedef {object} OptionSpec
|
|
19
|
+
* @property {string} type - Sequence type of the value, e.g. "xs:boolean"
|
|
20
|
+
* @property {*} [default] - Value when the option is absent
|
|
21
|
+
* @property {string[]} [values] - Allowed values of a string option
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Reads an option map.
|
|
26
|
+
* @param {import("../items/map.js").XdmMap|undefined} map - The options
|
|
27
|
+
* argument, undefined for the arity without it
|
|
28
|
+
* @param {Record<string, OptionSpec>} specs - Known options by key
|
|
29
|
+
* @param {string} invalidCode - Error code of a value outside `values`
|
|
30
|
+
* (FOJS0005 for the JSON functions)
|
|
31
|
+
* @returns {Record<string, *>} option values by key: the JS value of
|
|
32
|
+
* single atomic values (boolean, string), the item itself for functions,
|
|
33
|
+
* `undefined` for absent options without default
|
|
34
|
+
* @throws {XPathError} XPTY0004 for a value of the wrong type,
|
|
35
|
+
* `invalidCode` for a value that is not allowed
|
|
36
|
+
*/
|
|
37
|
+
export function readOptions(map, specs, invalidCode) {
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const [key, spec] of Object.entries(specs)) {
|
|
40
|
+
const value = map?.get(stringItem(key));
|
|
41
|
+
if (value === undefined) {
|
|
42
|
+
result[key] = spec.default;
|
|
43
|
+
continue;
|
|
44
|
+
}
|
|
45
|
+
const [item] = coerce(value, sequenceTypeOf(spec.type), {
|
|
46
|
+
what: `option "${key}"`,
|
|
47
|
+
});
|
|
48
|
+
const plain = item?.value === undefined ? item : item.value;
|
|
49
|
+
if (spec.values && !spec.values.includes(plain)) {
|
|
50
|
+
throw new XPathError(
|
|
51
|
+
invalidCode,
|
|
52
|
+
`Invalid value "${plain}" of option "${key}"`,
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
result[key] = plain;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:parse-ietf-date (F&O 3.1 section 9.9.1): the date formats of HTTP
|
|
3
|
+
* (RFC 822/1123, RFC 850 and asctime) to xs:dateTime, case-insensitive,
|
|
4
|
+
* with the liberal grammar of the specification.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/functions/parseIetfDate
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { XPathError } from "../errors.js";
|
|
10
|
+
import { fromLexical } from "../xdm/lexical.js";
|
|
11
|
+
import { define } from "./support.js";
|
|
12
|
+
|
|
13
|
+
const MONTHS = "jan feb mar apr may jun jul aug sep oct nov dec".split(" ");
|
|
14
|
+
const ZONES = {
|
|
15
|
+
ut: 0,
|
|
16
|
+
utc: 0,
|
|
17
|
+
gmt: 0,
|
|
18
|
+
est: -5,
|
|
19
|
+
edt: -4,
|
|
20
|
+
cst: -6,
|
|
21
|
+
cdt: -5,
|
|
22
|
+
mst: -7,
|
|
23
|
+
mdt: -6,
|
|
24
|
+
pst: -8,
|
|
25
|
+
pdt: -7,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const S = "[\\t\\n\\r ]+";
|
|
29
|
+
const DAY_NAME =
|
|
30
|
+
"(?:monday|tuesday|wednesday|thursday|friday|saturday|sunday|mon|tue|wed|thu|fri|sat|sun)";
|
|
31
|
+
const MONTH = `(${MONTHS.join("|")})`;
|
|
32
|
+
const DSEP = `(?:${S}|(?:${S})?-(?:${S})?)`;
|
|
33
|
+
const TZ_NAME = "(?:utc|ut|gmt|est|edt|cst|cdt|mst|mdt|pst|pdt)";
|
|
34
|
+
const TZ_OFFSET = `([+-])(\\d\\d?:(?:\\d\\d)?|\\d{1,4})(?:(?:${S})?\\((?:${S})?${TZ_NAME}(?:${S})?\\))?`;
|
|
35
|
+
const TIME = `(\\d\\d?):(\\d\\d)(?::(\\d\\d(?:\\.\\d+)?))?(?:(?:${S})?(?:(${TZ_NAME})|${TZ_OFFSET}))?`;
|
|
36
|
+
const YEAR = "(\\d\\d(?:\\d\\d)?)";
|
|
37
|
+
const DATE_SPEC = `(\\d\\d?)${DSEP}${MONTH}${DSEP}${YEAR}${S}${TIME}`;
|
|
38
|
+
const ASC_TIME = `${MONTH}${DSEP}(\\d\\d?)${S}${TIME}${S}${YEAR}`;
|
|
39
|
+
const INPUT = new RegExp(
|
|
40
|
+
`^(?:${S})?(?:${DAY_NAME},?${S})?(?:${DATE_SPEC}|${ASC_TIME})(?:${S})?$`,
|
|
41
|
+
"i",
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
/** @param {string|number} n @param {number} width @returns {string} */
|
|
45
|
+
const pad = (n, width) => String(n).padStart(width, "0");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The timezone of a parsed date: offset, else zone name, else UTC.
|
|
49
|
+
* @param {string|undefined} name
|
|
50
|
+
* @param {string|undefined} sign
|
|
51
|
+
* @param {string|undefined} offset - "H", "HH", "HMM", "HHMM" or "H:MM"
|
|
52
|
+
* @returns {string} "Z" or "+HH:MM"
|
|
53
|
+
*/
|
|
54
|
+
function timezone(name, sign, offset) {
|
|
55
|
+
if (offset !== undefined) {
|
|
56
|
+
const [hours, minutes = "00"] = offset.includes(":")
|
|
57
|
+
? offset.split(":")
|
|
58
|
+
: offset.length > 2
|
|
59
|
+
? [offset.slice(0, -2), offset.slice(-2)]
|
|
60
|
+
: [offset];
|
|
61
|
+
return `${sign}${pad(hours, 2)}:${pad(minutes || "00", 2)}`;
|
|
62
|
+
}
|
|
63
|
+
const hours = ZONES[name?.toLowerCase()] ?? 0;
|
|
64
|
+
return hours === 0 ? "Z" : `-${pad(-hours, 2)}:00`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Parses an IETF date.
|
|
69
|
+
* @param {string} text
|
|
70
|
+
* @returns {import("../xdm/atomic.js").AtomicValue} an xs:dateTime
|
|
71
|
+
* @throws {XPathError} FORG0010 when the text does not match the grammar
|
|
72
|
+
* or is not a valid date and time
|
|
73
|
+
*/
|
|
74
|
+
export function parseIetfDate(text) {
|
|
75
|
+
const match = INPUT.exec(text);
|
|
76
|
+
if (!match) throw new XPathError("FORG0010", `Invalid IETF date "${text}"`);
|
|
77
|
+
// groups: datespec 1-9 (day, month, year, time 6), asctime 10-18
|
|
78
|
+
// (month, day, time 6, year)
|
|
79
|
+
const spec = match[1] !== undefined;
|
|
80
|
+
const g = spec ? match.slice(1, 10) : match.slice(10, 19);
|
|
81
|
+
const [day, month, year] = spec ? g : [g[1], g[0], g[8]];
|
|
82
|
+
const [hours, minutes, seconds = "00", name, sign, offset] = spec
|
|
83
|
+
? g.slice(3, 9)
|
|
84
|
+
: g.slice(2, 8);
|
|
85
|
+
const fullYear = year.length === 2 ? `19${year}` : year;
|
|
86
|
+
const lexical =
|
|
87
|
+
`${fullYear}-${pad(MONTHS.indexOf(month.toLowerCase()) + 1, 2)}-` +
|
|
88
|
+
`${pad(day, 2)}T${pad(hours, 2)}:${minutes}:${pad(seconds, 2)}` +
|
|
89
|
+
timezone(name, sign, offset);
|
|
90
|
+
try {
|
|
91
|
+
return fromLexical("xs:dateTime", lexical);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
throw new XPathError("FORG0010", `Invalid IETF date "${text}"`, {
|
|
94
|
+
cause: error,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Function definitions. */
|
|
100
|
+
export const parseIetfDateFunctions = [
|
|
101
|
+
define("parse-ietf-date", ["xs:string?"], "xs:dateTime?", ([value]) =>
|
|
102
|
+
value.length ? [parseIetfDate(value[0].value)] : [],
|
|
103
|
+
),
|
|
104
|
+
];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:parse-xml and fn:parse-xml-fragment (F&O 3.1 sections 14.6.2 and
|
|
3
|
+
* 14.6.3), through the dynamic context hook `parseXml` (see
|
|
4
|
+
* xpath/eval/resources.js). The documents have the static base URI as
|
|
5
|
+
* base URI and no document URI; a fragment is a document fragment (a
|
|
6
|
+
* document node that may hold several elements and text).
|
|
7
|
+
*
|
|
8
|
+
* @module @tradik/xslt3/functions/parseXml
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { XPathError } from "../errors.js";
|
|
12
|
+
import { setBaseUri } from "./baseUris.js";
|
|
13
|
+
import { define } from "./support.js";
|
|
14
|
+
|
|
15
|
+
/** A text declaration: optional version, required encoding. */
|
|
16
|
+
const TEXT_DECLARATION =
|
|
17
|
+
/^<\?xml(?:\s+version\s*=\s*(["'])1\.\d+\1)?\s+encoding\s*=\s*(["'])[A-Za-z][\w.-]*\2\s*\?>/;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Parses with the hook, reporting failures as FODC0006.
|
|
21
|
+
* @param {string} text
|
|
22
|
+
* @param {object} context
|
|
23
|
+
* @returns {Document}
|
|
24
|
+
*/
|
|
25
|
+
function parse(text, context) {
|
|
26
|
+
try {
|
|
27
|
+
return context.parseXml(text, context.staticBaseUri);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
if (error.code === "FODC0006") throw error;
|
|
30
|
+
throw new XPathError("FODC0006", "The string is not well-formed XML", {
|
|
31
|
+
cause: error,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* fn:parse-xml-fragment of a string: the content of an external parsed
|
|
38
|
+
* entity, parsed inside a wrapper element.
|
|
39
|
+
* @param {string} text
|
|
40
|
+
* @param {object} context
|
|
41
|
+
* @returns {DocumentFragment}
|
|
42
|
+
*/
|
|
43
|
+
function parseFragment(text, context) {
|
|
44
|
+
let content = text;
|
|
45
|
+
if (/^<\?xml[\s?]/.test(text)) {
|
|
46
|
+
const declaration = TEXT_DECLARATION.exec(text);
|
|
47
|
+
if (!declaration) {
|
|
48
|
+
throw new XPathError("FODC0006", "Invalid text declaration");
|
|
49
|
+
}
|
|
50
|
+
content = text.slice(declaration[0].length);
|
|
51
|
+
}
|
|
52
|
+
const document = parse(`<fragment>${content}</fragment>`, context);
|
|
53
|
+
const fragment = document.createDocumentFragment();
|
|
54
|
+
const wrapper = document.documentElement;
|
|
55
|
+
while (wrapper.firstChild) fragment.appendChild(wrapper.firstChild);
|
|
56
|
+
return setBaseUri(fragment, context.staticBaseUri);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Function definitions. */
|
|
60
|
+
export const parseXmlFunctions = [
|
|
61
|
+
define(
|
|
62
|
+
"parse-xml",
|
|
63
|
+
["xs:string?"],
|
|
64
|
+
"document-node(element(*))?",
|
|
65
|
+
([arg], context) =>
|
|
66
|
+
arg.length
|
|
67
|
+
? [setBaseUri(parse(arg[0].value, context), context.staticBaseUri)]
|
|
68
|
+
: [],
|
|
69
|
+
),
|
|
70
|
+
define(
|
|
71
|
+
"parse-xml-fragment",
|
|
72
|
+
["xs:string?"],
|
|
73
|
+
"document-node()?",
|
|
74
|
+
([arg], context) =>
|
|
75
|
+
arg.length ? [parseFragment(arg[0].value, context)] : [],
|
|
76
|
+
),
|
|
77
|
+
];
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions on QNames that need no node (F&O 3.1 section 10.2): QName,
|
|
3
|
+
* prefix-from-QName, local-name-from-QName and namespace-uri-from-QName.
|
|
4
|
+
* fn:resolve-QName and fn:namespace-uri-for-prefix need in-scope
|
|
5
|
+
* namespaces of an element and belong to the evaluator.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/functions/qnames
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../errors.js";
|
|
11
|
+
import { AtomicValue } from "../xdm/atomic.js";
|
|
12
|
+
import { QNameValue } from "../xdm/qname.js";
|
|
13
|
+
import { namePatterns, types } from "../xdm/types.js";
|
|
14
|
+
import { define } from "./support.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* fn:QName on strings.
|
|
18
|
+
* @param {string} uri - "" for no namespace
|
|
19
|
+
* @param {string} lexical - "prefix:local" or "local"
|
|
20
|
+
* @returns {QNameValue}
|
|
21
|
+
* @throws {XPathError} FOCA0002 for an invalid lexical QName or a prefix
|
|
22
|
+
* without namespace
|
|
23
|
+
*/
|
|
24
|
+
export function makeQName(uri, lexical) {
|
|
25
|
+
const parts = lexical.split(":");
|
|
26
|
+
if (
|
|
27
|
+
parts.length > 2 ||
|
|
28
|
+
!parts.every((part) => namePatterns.ncName.test(part)) ||
|
|
29
|
+
(parts.length === 2 && uri === "")
|
|
30
|
+
) {
|
|
31
|
+
throw new XPathError("FOCA0002", `Invalid QName "${lexical}"`);
|
|
32
|
+
}
|
|
33
|
+
const [prefix, local] = parts.length === 2 ? parts : ["", parts[0]];
|
|
34
|
+
return new QNameValue(uri, local, prefix);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Declares an accessor of an optional xs:QName.
|
|
39
|
+
* @param {string} local
|
|
40
|
+
* @param {string} returns
|
|
41
|
+
* @param {(q: QNameValue) => AtomicValue|null} access
|
|
42
|
+
* @returns {import("./support.js").FunctionDefinition}
|
|
43
|
+
*/
|
|
44
|
+
const accessor = (local, returns, access) =>
|
|
45
|
+
define(local, ["xs:QName?"], returns, ([q]) => {
|
|
46
|
+
const result = q.length === 0 ? null : access(q[0].value);
|
|
47
|
+
return result === null ? [] : [result];
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
/** @type {import("./support.js").FunctionDefinition[]} */
|
|
51
|
+
export const qnameFunctions = [
|
|
52
|
+
define("QName", ["xs:string?", "xs:string"], "xs:QName", ([uri, [q]]) => [
|
|
53
|
+
new AtomicValue(types.QName, makeQName(uri[0]?.value ?? "", q.value)),
|
|
54
|
+
]),
|
|
55
|
+
accessor("prefix-from-QName", "xs:NCName?", (q) =>
|
|
56
|
+
q.prefix === "" ? null : new AtomicValue(types.NCName, q.prefix),
|
|
57
|
+
),
|
|
58
|
+
accessor(
|
|
59
|
+
"local-name-from-QName",
|
|
60
|
+
"xs:NCName?",
|
|
61
|
+
(q) => new AtomicValue(types.NCName, q.localName),
|
|
62
|
+
),
|
|
63
|
+
accessor(
|
|
64
|
+
"namespace-uri-from-QName",
|
|
65
|
+
"xs:anyURI?",
|
|
66
|
+
(q) => new AtomicValue(types.anyURI, q.namespaceURI),
|
|
67
|
+
),
|
|
68
|
+
];
|