@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,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The function library: definitions of built-in or extension functions,
|
|
3
|
+
* looked up by expanded name and arity.
|
|
4
|
+
*
|
|
5
|
+
* Function modules export arrays of definitions:
|
|
6
|
+
*
|
|
7
|
+
* ```js
|
|
8
|
+
* export const stringFunctions = [{
|
|
9
|
+
* local: "upper-case",
|
|
10
|
+
* params: ["xs:string?"],
|
|
11
|
+
* returns: "xs:string",
|
|
12
|
+
* impl: ([arg]) => [stringItem((arg[0]?.value ?? "").toUpperCase())],
|
|
13
|
+
* }];
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* The evaluator applies the function conversion rules (atomization,
|
|
17
|
+
* xs:untypedAtomic casting, numeric promotion, cardinality checks) to the
|
|
18
|
+
* arguments before calling `impl`, so `impl` receives sequences of the
|
|
19
|
+
* declared types.
|
|
20
|
+
*
|
|
21
|
+
* @module @tradik/xslt3/functions/registry
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import { NS } from "./signatures.js";
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {object} FunctionDefinition
|
|
28
|
+
* @property {string} [namespace] - Default
|
|
29
|
+
* "http://www.w3.org/2005/xpath-functions"; math:
|
|
30
|
+
* ".../xpath-functions/math", map: ".../xpath-functions/map", array:
|
|
31
|
+
* ".../xpath-functions/array"
|
|
32
|
+
* @property {string} local - Local name, e.g. "upper-case"
|
|
33
|
+
* @property {string[]} params - Parameter sequence types in XPath syntax,
|
|
34
|
+
* e.g. ["xs:string?", "item()*"]; the arity is params.length
|
|
35
|
+
* @property {string} returns - Result sequence type
|
|
36
|
+
* @property {boolean} [variadic] - The last parameter repeats (fn:concat):
|
|
37
|
+
* any arity from params.length up is accepted
|
|
38
|
+
* @property {boolean} [focus] - Needs the focus (context item, position,
|
|
39
|
+
* size)
|
|
40
|
+
* @property {(args: Array<Array>, context: DynamicContext) => Array} impl
|
|
41
|
+
* - Implementation: one sequence per argument, returns a sequence
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The context passed to implementations.
|
|
46
|
+
* @typedef {object} DynamicContext
|
|
47
|
+
* @property {number} implicitTimezone - Minutes
|
|
48
|
+
* @property {import("../xdm/datetime.js").DateTimeValue} currentDateTime
|
|
49
|
+
* @property {string} defaultCollation - Collation URI
|
|
50
|
+
* @property {string|undefined} staticBaseUri
|
|
51
|
+
* @property {string} defaultLanguage - "en"
|
|
52
|
+
* @property {*} contextItem - Focus (focus functions only), undefined when
|
|
53
|
+
* absent
|
|
54
|
+
* @property {number} position
|
|
55
|
+
* @property {number} size
|
|
56
|
+
* @property {(uri: string) => Node} loadDocument - Loads a document
|
|
57
|
+
* (FODC0002 when it cannot)
|
|
58
|
+
* @property {(href: string, encoding?: string) => string} loadText - Loads
|
|
59
|
+
* and decodes a text resource, href resolved against the static base URI
|
|
60
|
+
* (FOUT1170, FOUT1190, FOUT1200; see xpath/eval/resources.js)
|
|
61
|
+
* @property {(text: string, baseUri?: string) => Document} parseXml -
|
|
62
|
+
* Parses an XML document (throws when it is not well-formed)
|
|
63
|
+
* @property {(href: string|null) => Array} collection - The items of a
|
|
64
|
+
* collection, null for the default one (FODC0002 when unknown)
|
|
65
|
+
* @property {() => Document} createDocument - A new empty document, for
|
|
66
|
+
* functions that build nodes
|
|
67
|
+
*/
|
|
68
|
+
|
|
69
|
+
/** A set of function definitions. */
|
|
70
|
+
export class FunctionLibrary {
|
|
71
|
+
/**
|
|
72
|
+
* @param {FunctionDefinition[]} definitions - Later definitions replace
|
|
73
|
+
* earlier ones of the same name and arity
|
|
74
|
+
*/
|
|
75
|
+
constructor(definitions) {
|
|
76
|
+
/** @type {Map<string, {fixed: Map<number, FunctionDefinition>, variadic: FunctionDefinition[]}>} */
|
|
77
|
+
this.byName = new Map();
|
|
78
|
+
this.definitions = definitions;
|
|
79
|
+
for (const definition of definitions) {
|
|
80
|
+
const key = FunctionLibrary.key(
|
|
81
|
+
definition.namespace ?? NS.fn,
|
|
82
|
+
definition.local,
|
|
83
|
+
);
|
|
84
|
+
let entry = this.byName.get(key);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
entry = { fixed: new Map(), variadic: [] };
|
|
87
|
+
this.byName.set(key, entry);
|
|
88
|
+
}
|
|
89
|
+
if (definition.variadic) entry.variadic.push(definition);
|
|
90
|
+
else entry.fixed.set(definition.params.length, definition);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} namespace
|
|
96
|
+
* @param {string} local
|
|
97
|
+
* @returns {string} lookup key (Clark notation)
|
|
98
|
+
*/
|
|
99
|
+
static key(namespace, local) {
|
|
100
|
+
return `{${namespace}}${local}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* @param {string} namespace - Namespace URI
|
|
105
|
+
* @param {string} local - Local name
|
|
106
|
+
* @param {number} arity - Number of arguments
|
|
107
|
+
* @returns {FunctionDefinition|null} the definition, null when unknown
|
|
108
|
+
*/
|
|
109
|
+
lookup(namespace, local, arity) {
|
|
110
|
+
const entry = this.byName.get(FunctionLibrary.key(namespace, local));
|
|
111
|
+
if (!entry) return null;
|
|
112
|
+
const fixed = entry.fixed.get(arity);
|
|
113
|
+
if (fixed) return fixed;
|
|
114
|
+
for (let i = entry.variadic.length - 1; i >= 0; i--) {
|
|
115
|
+
if (arity >= entry.variadic[i].params.length) return entry.variadic[i];
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* @param {...FunctionDefinition[]} definitionArrays - More modules
|
|
122
|
+
* @returns {FunctionLibrary} a library with these definitions added
|
|
123
|
+
*/
|
|
124
|
+
extend(...definitionArrays) {
|
|
125
|
+
return new FunctionLibrary([
|
|
126
|
+
...this.definitions,
|
|
127
|
+
...definitionArrays.flat(),
|
|
128
|
+
]);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Creates a function library from modules.
|
|
134
|
+
* @param {...FunctionDefinition[]} definitionArrays - Arrays of definitions
|
|
135
|
+
* @returns {FunctionLibrary}
|
|
136
|
+
* @example createFunctionLibrary(coreFunctions, stringFunctions)
|
|
137
|
+
*/
|
|
138
|
+
export function createFunctionLibrary(...definitionArrays) {
|
|
139
|
+
return new FunctionLibrary(definitionArrays.flat());
|
|
140
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Functions on sequences (F&O 3.1 sections 14.1 to 14.2.6) that need no
|
|
3
|
+
* node access: head, tail, insert-before, remove, reverse, subsequence,
|
|
4
|
+
* unordered, index-of, distinct-values, the cardinality checks and
|
|
5
|
+
* deep-equal. fn:empty, fn:exists and fn:count belong to the core library.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/functions/sequences
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../errors.js";
|
|
11
|
+
import { compareAtomic, deepEqualAtomic } from "../xdm/compare.js";
|
|
12
|
+
import { DateTimeValue } from "../xdm/datetime.js";
|
|
13
|
+
import { timelineSeconds } from "../xdm/timeline.js";
|
|
14
|
+
import { isNumericType } from "../xdm/types.js";
|
|
15
|
+
import { collationArg } from "./collations.js";
|
|
16
|
+
import { deepEqualSequences } from "./deepEqual.js";
|
|
17
|
+
import { selectRange } from "./strings.js";
|
|
18
|
+
import { booleanItem, define, integerItem } from "./support.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Comparison options for a collation argument and the context.
|
|
22
|
+
* @param {Array<*>|undefined} collation
|
|
23
|
+
* @param {object} context
|
|
24
|
+
* @returns {{collation: Function, implicitTimezone: number, key: Function|null}}
|
|
25
|
+
*/
|
|
26
|
+
function compareOptions(collation, context) {
|
|
27
|
+
const resolved = collationArg(collation, context);
|
|
28
|
+
return {
|
|
29
|
+
collation: resolved.compare,
|
|
30
|
+
implicitTimezone: context.implicitTimezone ?? 0,
|
|
31
|
+
key: resolved.key,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Bucket key of an atomic value for distinct-values: values that may be
|
|
37
|
+
* equal share a key; equality is then checked with deepEqualAtomic.
|
|
38
|
+
* @param {import("../xdm/atomic.js").AtomicValue} item
|
|
39
|
+
* @param {object} options - From {@link compareOptions}
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
function bucketKey(item, options) {
|
|
43
|
+
const { type, value } = item;
|
|
44
|
+
const primitive = type.primitive.localName;
|
|
45
|
+
// equal numbers of different types share the float nearest to them
|
|
46
|
+
if (isNumericType(type)) return `n${Math.fround(Number(value.toString()))}`;
|
|
47
|
+
if (["string", "anyURI", "untypedAtomic"].includes(primitive)) {
|
|
48
|
+
return options.key ? `s${options.key(value)}` : "s";
|
|
49
|
+
}
|
|
50
|
+
if (primitive === "duration") return `d${value.seconds}`;
|
|
51
|
+
if (value instanceof DateTimeValue) {
|
|
52
|
+
return `${primitive}${timelineSeconds(value, options.implicitTimezone)}`;
|
|
53
|
+
}
|
|
54
|
+
return primitive;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* fn:distinct-values.
|
|
59
|
+
* @param {Array<*>[]} args - [$arg, $collation?]
|
|
60
|
+
* @param {object} context
|
|
61
|
+
* @returns {Array<*>}
|
|
62
|
+
*/
|
|
63
|
+
function distinctValues([items, collation], context) {
|
|
64
|
+
const options = compareOptions(collation, context);
|
|
65
|
+
const buckets = new Map();
|
|
66
|
+
const result = [];
|
|
67
|
+
for (const item of items) {
|
|
68
|
+
const key = bucketKey(item, options);
|
|
69
|
+
const bucket = buckets.get(key) ?? [];
|
|
70
|
+
if (!bucket.some((seen) => deepEqualAtomic(seen, item, options))) {
|
|
71
|
+
bucket.push(item);
|
|
72
|
+
buckets.set(key, bucket);
|
|
73
|
+
result.push(item);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* fn:index-of: positions of the items `eq` to the search value
|
|
81
|
+
* (incomparable values and NaN are never equal).
|
|
82
|
+
* @param {Array<*>[]} args - [$seq, $search, $collation?]
|
|
83
|
+
* @param {object} context
|
|
84
|
+
* @returns {Array<*>}
|
|
85
|
+
*/
|
|
86
|
+
function indexOf([items, [search], collation], context) {
|
|
87
|
+
const options = compareOptions(collation, context);
|
|
88
|
+
const positions = [];
|
|
89
|
+
items.forEach((item, i) => {
|
|
90
|
+
try {
|
|
91
|
+
if (compareAtomic(item, search, options).order === 0) {
|
|
92
|
+
positions.push(integerItem(i + 1));
|
|
93
|
+
}
|
|
94
|
+
} catch (error) {
|
|
95
|
+
if (!(error instanceof XPathError)) throw error;
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
return positions;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Declares a cardinality check.
|
|
103
|
+
* @param {string} local
|
|
104
|
+
* @param {string} returns
|
|
105
|
+
* @param {(n: number) => boolean} accept
|
|
106
|
+
* @param {string} code - Error raised otherwise
|
|
107
|
+
* @returns {import("./support.js").FunctionDefinition}
|
|
108
|
+
*/
|
|
109
|
+
const cardinality = (local, returns, accept, code) =>
|
|
110
|
+
define(local, ["item()*"], returns, ([items]) => {
|
|
111
|
+
if (!accept(items.length)) {
|
|
112
|
+
throw new XPathError(code, `${local} called with ${items.length} items`);
|
|
113
|
+
}
|
|
114
|
+
return items;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
/** @param {Array<*>[]} args @returns {Array<*>} fn:subsequence */
|
|
118
|
+
const subsequence = ([items, [start], length]) =>
|
|
119
|
+
items.slice(...selectRange(items.length, start.value, length?.[0].value));
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* fn:deep-equal; nodes are compared by deepEqual.js, maps, arrays and
|
|
123
|
+
* function items by the `context.deepEqualItem` hook.
|
|
124
|
+
* @param {Array<*>[]} args - [$p1, $p2, $collation?]
|
|
125
|
+
* @param {object} context
|
|
126
|
+
* @returns {Array<*>}
|
|
127
|
+
*/
|
|
128
|
+
function deepEqual([a, b, collation], context) {
|
|
129
|
+
const options = compareOptions(collation, context);
|
|
130
|
+
options.deepEqualItem = context.deepEqualItem;
|
|
131
|
+
return [booleanItem(deepEqualSequences(a, b, options))];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const ITEMS = "item()*";
|
|
135
|
+
const ATOMS = "xs:anyAtomicType*";
|
|
136
|
+
|
|
137
|
+
/** @type {import("./support.js").FunctionDefinition[]} */
|
|
138
|
+
export const sequenceFunctions = [
|
|
139
|
+
define("head", [ITEMS], "item()?", ([items]) => items.slice(0, 1)),
|
|
140
|
+
define("tail", [ITEMS], ITEMS, ([items]) => items.slice(1)),
|
|
141
|
+
define(
|
|
142
|
+
"insert-before",
|
|
143
|
+
[ITEMS, "xs:integer", ITEMS],
|
|
144
|
+
ITEMS,
|
|
145
|
+
([target, [position], inserts]) => {
|
|
146
|
+
const p = position.value;
|
|
147
|
+
const at =
|
|
148
|
+
p < 1n ? 0 : p > BigInt(target.length) ? target.length : Number(p) - 1;
|
|
149
|
+
return [...target.slice(0, at), ...inserts, ...target.slice(at)];
|
|
150
|
+
},
|
|
151
|
+
),
|
|
152
|
+
define("remove", [ITEMS, "xs:integer"], ITEMS, ([target, [position]]) =>
|
|
153
|
+
target.filter((_item, i) => BigInt(i + 1) !== position.value),
|
|
154
|
+
),
|
|
155
|
+
define("reverse", [ITEMS], ITEMS, ([items]) => [...items].reverse()),
|
|
156
|
+
define("subsequence", [ITEMS, "xs:double"], ITEMS, subsequence),
|
|
157
|
+
define("subsequence", [ITEMS, "xs:double", "xs:double"], ITEMS, subsequence),
|
|
158
|
+
define("unordered", [ITEMS], ITEMS, ([items]) => items),
|
|
159
|
+
define("index-of", [ATOMS, "xs:anyAtomicType"], "xs:integer*", indexOf),
|
|
160
|
+
define(
|
|
161
|
+
"index-of",
|
|
162
|
+
[ATOMS, "xs:anyAtomicType", "xs:string"],
|
|
163
|
+
"xs:integer*",
|
|
164
|
+
indexOf,
|
|
165
|
+
),
|
|
166
|
+
define("distinct-values", [ATOMS], ATOMS, distinctValues),
|
|
167
|
+
define("distinct-values", [ATOMS, "xs:string"], ATOMS, distinctValues),
|
|
168
|
+
cardinality("zero-or-one", "item()?", (n) => n <= 1, "FORG0003"),
|
|
169
|
+
cardinality("one-or-more", "item()+", (n) => n >= 1, "FORG0004"),
|
|
170
|
+
cardinality("exactly-one", "item()", (n) => n === 1, "FORG0005"),
|
|
171
|
+
define("deep-equal", [ITEMS, ITEMS], "xs:boolean", deepEqual),
|
|
172
|
+
define("deep-equal", [ITEMS, ITEMS, "xs:string"], "xs:boolean", deepEqual),
|
|
173
|
+
];
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:serialize (F&O 3.1 section 14.6.3): the serialization of a sequence
|
|
3
|
+
* as a string, with parameters given as an output:serialization-parameters
|
|
4
|
+
* element or as a map. The defaults are those of the F&O table: the xml
|
|
5
|
+
* method, no indentation, and no XML declaration.
|
|
6
|
+
*
|
|
7
|
+
* @module @tradik/xslt3/functions/serializeFunction
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { XPathError } from "../errors.js";
|
|
11
|
+
import { serialize } from "../serialize/index.js";
|
|
12
|
+
import { parametersFromElement } from "../serialize/params/fromElement.js";
|
|
13
|
+
import { parametersFromMap } from "../serialize/params/fromMap.js";
|
|
14
|
+
import { isMap, isNode } from "../xdm/index.js";
|
|
15
|
+
import { define, stringItem } from "./support.js";
|
|
16
|
+
|
|
17
|
+
/** Defaults of fn:serialize that differ from the serializer's. */
|
|
18
|
+
const FN_DEFAULTS = Object.freeze({ "omit-xml-declaration": true });
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The parameters of the optional second argument.
|
|
22
|
+
* @param {*} [options] - Element, map or undefined
|
|
23
|
+
* @returns {Record<string, *>}
|
|
24
|
+
* @throws {XPathError} XPTY0004 for other items
|
|
25
|
+
*/
|
|
26
|
+
function parametersOf(options) {
|
|
27
|
+
if (options === undefined) return {};
|
|
28
|
+
if (isMap(options)) return parametersFromMap(options);
|
|
29
|
+
if (isNode(options)) return parametersFromElement(options);
|
|
30
|
+
throw new XPathError(
|
|
31
|
+
"XPTY0004",
|
|
32
|
+
"The parameters of fn:serialize must be an element or a map",
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @param {Array} sequence
|
|
38
|
+
* @param {*} [options]
|
|
39
|
+
* @returns {Array} the serialization as an xs:string
|
|
40
|
+
*/
|
|
41
|
+
const serializeSequence = (sequence, options) => [
|
|
42
|
+
stringItem(serialize(sequence, { ...FN_DEFAULTS, ...parametersOf(options) })),
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/** fn:serialize#1 and fn:serialize#2. */
|
|
46
|
+
export const serializeFunctions = [
|
|
47
|
+
define("serialize", ["item()*"], "xs:string", ([arg]) =>
|
|
48
|
+
serializeSequence(arg),
|
|
49
|
+
),
|
|
50
|
+
define("serialize", ["item()*", "item()?"], "xs:string", ([arg, options]) =>
|
|
51
|
+
serializeSequence(arg, options[0]),
|
|
52
|
+
),
|
|
53
|
+
];
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Names and arities of the functions of XPath and XQuery Functions and
|
|
3
|
+
* Operators 3.1 (fn:, math:, map:, array:), used by static analysis to tell
|
|
4
|
+
* a known function from an unknown one (XPST0017) before every function
|
|
5
|
+
* has an implementation registered. A function listed here but missing from
|
|
6
|
+
* the function library compiles, and raises XPST0017 "not implemented" when
|
|
7
|
+
* it is called.
|
|
8
|
+
*
|
|
9
|
+
* @module @tradik/xslt3/functions/signatures
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Namespace URIs of the function libraries. */
|
|
13
|
+
export const NS = Object.freeze({
|
|
14
|
+
fn: "http://www.w3.org/2005/xpath-functions",
|
|
15
|
+
math: "http://www.w3.org/2005/xpath-functions/math",
|
|
16
|
+
map: "http://www.w3.org/2005/xpath-functions/map",
|
|
17
|
+
array: "http://www.w3.org/2005/xpath-functions/array",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Per namespace: "name arities" entries; arities are "1", "0-2", "2,5" or
|
|
22
|
+
* "2+" (variadic).
|
|
23
|
+
*/
|
|
24
|
+
const CATALOG = {
|
|
25
|
+
fn: `abs 1|adjust-date-to-timezone 1-2|adjust-dateTime-to-timezone 1-2|
|
|
26
|
+
adjust-time-to-timezone 1-2|analyze-string 2-3|apply 2|
|
|
27
|
+
available-environment-variables 0|avg 1|base-uri 0-1|boolean 1|ceiling 1|
|
|
28
|
+
codepoint-equal 2|codepoints-to-string 1|collation-key 1-2|collection 0-1|
|
|
29
|
+
compare 2-3|concat 2+|contains 2-3|contains-token 2-3|count 1|current-date 0|
|
|
30
|
+
current-dateTime 0|current-time 0|data 0-1|dateTime 2|day-from-date 1|
|
|
31
|
+
day-from-dateTime 1|days-from-duration 1|deep-equal 2-3|default-collation 0|
|
|
32
|
+
default-language 0|distinct-values 1-2|doc 1|doc-available 1|document-uri 0-1|
|
|
33
|
+
element-with-id 1-2|empty 1|encode-for-uri 1|ends-with 2-3|
|
|
34
|
+
environment-variable 1|error 0-3|escape-html-uri 1|exactly-one 1|exists 1|
|
|
35
|
+
false 0|filter 2|floor 1|fold-left 3|fold-right 3|for-each 2|for-each-pair 3|
|
|
36
|
+
format-date 2,5|format-dateTime 2,5|format-integer 2-3|format-number 2-3|
|
|
37
|
+
format-time 2,5|function-arity 1|function-lookup 2|function-name 1|
|
|
38
|
+
generate-id 0-1|has-children 0-1|head 1|hours-from-dateTime 1|
|
|
39
|
+
hours-from-duration 1|hours-from-time 1|id 1-2|idref 1-2|implicit-timezone 0|
|
|
40
|
+
in-scope-prefixes 1|index-of 2-3|innermost 1|insert-before 3|iri-to-uri 1|
|
|
41
|
+
json-doc 1-2|json-to-xml 1-2|lang 1-2|last 0|load-xquery-module 1-2|
|
|
42
|
+
local-name 0-1|local-name-from-QName 1|lower-case 1|matches 2-3|max 1-2|
|
|
43
|
+
min 1-2|minutes-from-dateTime 1|minutes-from-duration 1|minutes-from-time 1|
|
|
44
|
+
month-from-date 1|month-from-dateTime 1|months-from-duration 1|name 0-1|
|
|
45
|
+
namespace-uri 0-1|namespace-uri-for-prefix 2|namespace-uri-from-QName 1|
|
|
46
|
+
nilled 0-1|node-name 0-1|normalize-space 0-1|normalize-unicode 1-2|not 1|
|
|
47
|
+
number 0-1|one-or-more 1|outermost 1|parse-ietf-date 1|parse-json 1-2|
|
|
48
|
+
parse-xml 1|parse-xml-fragment 1|path 0-1|position 0|prefix-from-QName 1|
|
|
49
|
+
QName 2|random-number-generator 0-1|remove 2|replace 3-4|resolve-QName 2|
|
|
50
|
+
resolve-uri 1-2|reverse 1|root 0-1|round 1-2|round-half-to-even 1-2|
|
|
51
|
+
seconds-from-dateTime 1|seconds-from-duration 1|seconds-from-time 1|
|
|
52
|
+
serialize 1-2|sort 1-3|starts-with 2-3|static-base-uri 0|string 0-1|
|
|
53
|
+
string-join 1-2|string-length 0-1|string-to-codepoints 1|subsequence 2-3|
|
|
54
|
+
substring 2-3|substring-after 2-3|substring-before 2-3|sum 1-2|tail 1|
|
|
55
|
+
timezone-from-date 1|timezone-from-dateTime 1|timezone-from-time 1|
|
|
56
|
+
tokenize 1-3|trace 1-2|transform 1|translate 3|true 0|unordered 1|
|
|
57
|
+
unparsed-text 1-2|unparsed-text-available 1-2|unparsed-text-lines 1-2|
|
|
58
|
+
upper-case 1|uri-collection 0-1|xml-to-json 1-2|year-from-date 1|
|
|
59
|
+
year-from-dateTime 1|years-from-duration 1|zero-or-one 1`,
|
|
60
|
+
math: `acos 1|asin 1|atan 1|atan2 2|cos 1|exp 1|exp10 1|log 1|log10 1|pi 0|
|
|
61
|
+
pow 2|sin 1|sqrt 1|tan 1`,
|
|
62
|
+
map: `contains 2|entry 2|find 2|for-each 2|get 2|keys 1|merge 1-2|put 3|
|
|
63
|
+
remove 2|size 1`,
|
|
64
|
+
array: `append 2|filter 2|flatten 1|fold-left 3|fold-right 3|for-each 2|
|
|
65
|
+
for-each-pair 3|get 2|head 1|insert-before 3|join 1|put 3|remove 2|reverse 1|
|
|
66
|
+
size 1|sort 1-3|subarray 2-3|tail 1`,
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Parses an arity specification.
|
|
71
|
+
* @param {string} spec - "1", "0-2", "2,5" or "2+"
|
|
72
|
+
* @returns {(arity: number) => boolean} membership test
|
|
73
|
+
*/
|
|
74
|
+
function aritySet(spec) {
|
|
75
|
+
if (spec.endsWith("+")) {
|
|
76
|
+
const min = Number(spec.slice(0, -1));
|
|
77
|
+
return (arity) => arity >= min;
|
|
78
|
+
}
|
|
79
|
+
const allowed = new Set();
|
|
80
|
+
for (const part of spec.split(",")) {
|
|
81
|
+
const [low, high = low] = part.split("-").map(Number);
|
|
82
|
+
for (let n = low; n <= high; n++) allowed.add(n);
|
|
83
|
+
}
|
|
84
|
+
return (arity) => allowed.has(arity);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** @type {Map<string, (arity: number) => boolean>} by Clark name */
|
|
88
|
+
const known = new Map();
|
|
89
|
+
for (const [prefix, text] of Object.entries(CATALOG)) {
|
|
90
|
+
for (const entry of text.replace(/\n/g, "").split("|")) {
|
|
91
|
+
const [name, spec] = entry.split(" ");
|
|
92
|
+
known.set(`{${NS[prefix]}}${name}`, aritySet(spec));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Whether F&O 3.1 defines a function with this name and arity.
|
|
98
|
+
* @param {string} namespace - Namespace URI
|
|
99
|
+
* @param {string} local - Local name
|
|
100
|
+
* @param {number} arity - Number of arguments
|
|
101
|
+
* @returns {boolean}
|
|
102
|
+
*/
|
|
103
|
+
export function isStandardFunction(namespace, local, arity) {
|
|
104
|
+
return known.get(`{${namespace}}${local}`)?.(arity) ?? false;
|
|
105
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fn:sort (F&O 3.1 section 16.2.4): a stable sort on sort keys, which are
|
|
3
|
+
* compared item by item (NaN first, a prefix before a longer key).
|
|
4
|
+
*
|
|
5
|
+
* @module @tradik/xslt3/functions/sort
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { XPathError } from "../errors.js";
|
|
9
|
+
import { cast } from "../xdm/cast.js";
|
|
10
|
+
import { compareAtomic } from "../xdm/compare.js";
|
|
11
|
+
import { atomize } from "../xdm/nodes.js";
|
|
12
|
+
import { types } from "../xdm/types.js";
|
|
13
|
+
import { collationArg } from "./collations.js";
|
|
14
|
+
|
|
15
|
+
/** @param {*} value @returns {boolean} whether an atomic value is NaN */
|
|
16
|
+
const isNaNValue = (value) =>
|
|
17
|
+
typeof value.value === "number" && Number.isNaN(value.value);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Compares two atomic values for sorting.
|
|
21
|
+
* @returns {number}
|
|
22
|
+
*/
|
|
23
|
+
function compareItems(a, b, options) {
|
|
24
|
+
if (isNaNValue(a) || isNaNValue(b)) {
|
|
25
|
+
return Number(isNaNValue(b)) - Number(isNaNValue(a));
|
|
26
|
+
}
|
|
27
|
+
const { order, ordered } = compareAtomic(a, b, options);
|
|
28
|
+
if (!ordered && order !== 0) {
|
|
29
|
+
throw new XPathError("XPTY0004", "Sort keys are not ordered");
|
|
30
|
+
}
|
|
31
|
+
return order;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Compares two sort keys (sequences of atomic values).
|
|
36
|
+
* @returns {number}
|
|
37
|
+
*/
|
|
38
|
+
export function compareKeys(a, b, options) {
|
|
39
|
+
const length = Math.min(a.length, b.length);
|
|
40
|
+
for (let i = 0; i < length; i++) {
|
|
41
|
+
const order = compareItems(a[i], b[i], options);
|
|
42
|
+
if (order !== 0) return order;
|
|
43
|
+
}
|
|
44
|
+
return a.length - b.length;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Sort key of an item: atomized, untyped values as strings.
|
|
49
|
+
* @param {Array} sequence
|
|
50
|
+
* @returns {Array}
|
|
51
|
+
*/
|
|
52
|
+
const sortKey = (sequence) =>
|
|
53
|
+
atomize(sequence).map((v) =>
|
|
54
|
+
v.type === types.untypedAtomic ? cast(v, types.string) : v,
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Sorts values by their sort keys (stable).
|
|
59
|
+
* @param {Array} values - Items (fn:sort) or members (array:sort)
|
|
60
|
+
* @param {Array|undefined} collation - Collation argument
|
|
61
|
+
* @param {*} f - Key function item, undefined for the values themselves
|
|
62
|
+
* @param {object} context
|
|
63
|
+
* @param {(value: *) => Array} asArgument - The argument of the key
|
|
64
|
+
* function for a value
|
|
65
|
+
* @returns {Array} the values in order
|
|
66
|
+
*/
|
|
67
|
+
export function sortByKeys(values, collation, f, context, asArgument) {
|
|
68
|
+
const options = {
|
|
69
|
+
...context.compareOptions,
|
|
70
|
+
collation: collationArg(collation, context).compare,
|
|
71
|
+
};
|
|
72
|
+
const keyed = values.map((value) => ({
|
|
73
|
+
value,
|
|
74
|
+
key: sortKey(f ? f.invoke([asArgument(value)]) : asArgument(value)),
|
|
75
|
+
}));
|
|
76
|
+
keyed.sort((x, y) => compareKeys(x.key, y.key, options));
|
|
77
|
+
return keyed.map(({ value }) => value);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* fn:sort.
|
|
82
|
+
* @returns {Array}
|
|
83
|
+
*/
|
|
84
|
+
const sort = ([input, collation, key], context) =>
|
|
85
|
+
sortByKeys(input, collation, key?.[0], context, (item) => [item]);
|
|
86
|
+
|
|
87
|
+
/** Function definitions. */
|
|
88
|
+
export const sortFunctions = [
|
|
89
|
+
{ local: "sort", params: ["item()*"], returns: "item()*", impl: sort },
|
|
90
|
+
{
|
|
91
|
+
local: "sort",
|
|
92
|
+
params: ["item()*", "xs:string?"],
|
|
93
|
+
returns: "item()*",
|
|
94
|
+
impl: sort,
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
local: "sort",
|
|
98
|
+
params: ["item()*", "xs:string?", "function(item()) as xs:anyAtomicType*"],
|
|
99
|
+
returns: "item()*",
|
|
100
|
+
impl: sort,
|
|
101
|
+
},
|
|
102
|
+
];
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Substring matching under a collation (F&O 3.1 section 5.5): contains,
|
|
3
|
+
* starts-with, ends-with, substring-before and substring-after, each with
|
|
4
|
+
* an optional collation argument.
|
|
5
|
+
*
|
|
6
|
+
* @module @tradik/xslt3/functions/stringMatch
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { collationArg, findSubstring } from "./collations.js";
|
|
10
|
+
import { booleanItem, define, stringArg, stringItem } from "./support.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Declares a function of the family in both arities.
|
|
14
|
+
* @param {string} local
|
|
15
|
+
* @param {string} returns
|
|
16
|
+
* @param {(text: string, search: string, collation: object) => *} match
|
|
17
|
+
* @returns {import("./support.js").FunctionDefinition[]}
|
|
18
|
+
*/
|
|
19
|
+
function family(local, returns, match) {
|
|
20
|
+
const impl = ([arg1, arg2, collation], context) => [
|
|
21
|
+
match(stringArg(arg1), stringArg(arg2), collationArg(collation, context)),
|
|
22
|
+
];
|
|
23
|
+
return [
|
|
24
|
+
define(local, ["xs:string?", "xs:string?"], returns, impl),
|
|
25
|
+
define(local, ["xs:string?", "xs:string?", "xs:string"], returns, impl),
|
|
26
|
+
];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const found = (anchor) => (text, search, collation) =>
|
|
30
|
+
booleanItem(findSubstring(collation, text, search, anchor) !== null);
|
|
31
|
+
|
|
32
|
+
/** @type {import("./support.js").FunctionDefinition[]} */
|
|
33
|
+
export const stringMatchFunctions = [
|
|
34
|
+
...family("contains", "xs:boolean", found(null)),
|
|
35
|
+
...family("starts-with", "xs:boolean", found("start")),
|
|
36
|
+
...family("ends-with", "xs:boolean", found("end")),
|
|
37
|
+
...family("substring-before", "xs:string", (text, search, collation) => {
|
|
38
|
+
const match = findSubstring(collation, text, search);
|
|
39
|
+
return stringItem(match ? text.slice(0, match[0]) : "");
|
|
40
|
+
}),
|
|
41
|
+
...family("substring-after", "xs:string", (text, search, collation) => {
|
|
42
|
+
const match = findSubstring(collation, text, search);
|
|
43
|
+
return stringItem(match ? text.slice(match[1]) : "");
|
|
44
|
+
}),
|
|
45
|
+
];
|