@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.
Files changed (286) hide show
  1. package/LICENSE.md +28 -0
  2. package/README.md +98 -2
  3. package/package.json +43 -4
  4. package/src/errors.js +20 -0
  5. package/src/functions/aggregates.js +144 -0
  6. package/src/functions/arraySort.js +33 -0
  7. package/src/functions/arrays.js +181 -0
  8. package/src/functions/attributeTypes.js +58 -0
  9. package/src/functions/baseUris.js +82 -0
  10. package/src/functions/collations.js +127 -0
  11. package/src/functions/collections.js +84 -0
  12. package/src/functions/context.js +83 -0
  13. package/src/functions/core.js +167 -0
  14. package/src/functions/datetime.js +203 -0
  15. package/src/functions/deepEqual.js +132 -0
  16. package/src/functions/diagnostics.js +88 -0
  17. package/src/functions/focus.js +34 -0
  18. package/src/functions/format/dateComponents.js +176 -0
  19. package/src/functions/format/datePicture.js +115 -0
  20. package/src/functions/format/decimalFormat.js +65 -0
  21. package/src/functions/format/digitPattern.js +145 -0
  22. package/src/functions/format/formatDateTime.js +204 -0
  23. package/src/functions/format/formatInteger.js +109 -0
  24. package/src/functions/format/formatNumber.js +165 -0
  25. package/src/functions/format/fractionFormat.js +80 -0
  26. package/src/functions/format/germanWords.js +89 -0
  27. package/src/functions/format/numberLanguages.js +41 -0
  28. package/src/functions/format/numberPicture.js +202 -0
  29. package/src/functions/format/numberWords.js +155 -0
  30. package/src/functions/format/place.js +74 -0
  31. package/src/functions/format/timezoneFormat.js +89 -0
  32. package/src/functions/higherOrder.js +106 -0
  33. package/src/functions/ids.js +127 -0
  34. package/src/functions/json/index.js +17 -0
  35. package/src/functions/json/jsonChars.js +110 -0
  36. package/src/functions/json/jsonParser.js +203 -0
  37. package/src/functions/json/jsonStrings.js +116 -0
  38. package/src/functions/json/jsonToXml.js +116 -0
  39. package/src/functions/json/parseJson.js +119 -0
  40. package/src/functions/json/xmlRepresentation.js +103 -0
  41. package/src/functions/json/xmlToJson.js +147 -0
  42. package/src/functions/library.js +47 -0
  43. package/src/functions/mapArrayEqual.js +41 -0
  44. package/src/functions/maps.js +152 -0
  45. package/src/functions/math.js +73 -0
  46. package/src/functions/nodePaths.js +181 -0
  47. package/src/functions/nodes.js +199 -0
  48. package/src/functions/numeric.js +148 -0
  49. package/src/functions/options.js +58 -0
  50. package/src/functions/parseIetfDate.js +104 -0
  51. package/src/functions/parseXml.js +77 -0
  52. package/src/functions/qnames.js +68 -0
  53. package/src/functions/random.js +128 -0
  54. package/src/functions/regex/analyzeString.js +144 -0
  55. package/src/functions/regex/charClass.js +112 -0
  56. package/src/functions/regex/compile.js +81 -0
  57. package/src/functions/regex/escapes.js +109 -0
  58. package/src/functions/regex/parser.js +169 -0
  59. package/src/functions/regex/reader.js +37 -0
  60. package/src/functions/regex/regexFunctions.js +140 -0
  61. package/src/functions/regex/translate.js +102 -0
  62. package/src/functions/regex/unicodeBlocks.js +194 -0
  63. package/src/functions/registry.js +140 -0
  64. package/src/functions/sequences.js +173 -0
  65. package/src/functions/serializeFunction.js +53 -0
  66. package/src/functions/signatures.js +105 -0
  67. package/src/functions/sort.js +102 -0
  68. package/src/functions/stringMatch.js +45 -0
  69. package/src/functions/strings.js +194 -0
  70. package/src/functions/stringsCore.js +66 -0
  71. package/src/functions/support.js +106 -0
  72. package/src/functions/textDecoding.js +117 -0
  73. package/src/functions/ucaCollation.js +127 -0
  74. package/src/functions/unparsedText.js +71 -0
  75. package/src/functions/uri.js +166 -0
  76. package/src/functions/xqueryModule.js +24 -0
  77. package/src/index.js +38 -0
  78. package/src/items/array.js +84 -0
  79. package/src/items/function.js +45 -0
  80. package/src/items/map.js +120 -0
  81. package/src/items/mapKey.js +82 -0
  82. package/src/serialize/index.js +129 -0
  83. package/src/serialize/markup/content.js +74 -0
  84. package/src/serialize/markup/dialect.js +178 -0
  85. package/src/serialize/markup/htmlData.js +70 -0
  86. package/src/serialize/markup/namespaces.js +145 -0
  87. package/src/serialize/markup/prolog.js +91 -0
  88. package/src/serialize/markup/writer.js +183 -0
  89. package/src/serialize/methods/adaptive.js +131 -0
  90. package/src/serialize/methods/json.js +158 -0
  91. package/src/serialize/methods/text.js +43 -0
  92. package/src/serialize/output/buffer.js +48 -0
  93. package/src/serialize/output/encoding.js +146 -0
  94. package/src/serialize/output/expander.js +204 -0
  95. package/src/serialize/params/fromElement.js +141 -0
  96. package/src/serialize/params/fromMap.js +128 -0
  97. package/src/serialize/params/names.js +69 -0
  98. package/src/serialize/params/settings.js +153 -0
  99. package/src/serialize/params/values.js +163 -0
  100. package/src/serialize/sequence.js +111 -0
  101. package/src/xdm/arithmetic.js +155 -0
  102. package/src/xdm/atomic.js +142 -0
  103. package/src/xdm/binary.js +92 -0
  104. package/src/xdm/calendar.js +71 -0
  105. package/src/xdm/cast.js +176 -0
  106. package/src/xdm/compare.js +192 -0
  107. package/src/xdm/datetime.js +206 -0
  108. package/src/xdm/decimal.js +200 -0
  109. package/src/xdm/duration.js +128 -0
  110. package/src/xdm/generalCompare.js +152 -0
  111. package/src/xdm/index.js +47 -0
  112. package/src/xdm/lexical.js +126 -0
  113. package/src/xdm/nodes.js +170 -0
  114. package/src/xdm/numeric.js +122 -0
  115. package/src/xdm/qname.js +65 -0
  116. package/src/xdm/strings.js +52 -0
  117. package/src/xdm/temporalArithmetic.js +180 -0
  118. package/src/xdm/timeline.js +68 -0
  119. package/src/xdm/types.js +178 -0
  120. package/src/xpath/eval/atomics.js +45 -0
  121. package/src/xpath/eval/axes.js +212 -0
  122. package/src/xpath/eval/calls.js +168 -0
  123. package/src/xpath/eval/coercion.js +146 -0
  124. package/src/xpath/eval/compiler.js +71 -0
  125. package/src/xpath/eval/constructors.js +129 -0
  126. package/src/xpath/eval/decimalFormats.js +93 -0
  127. package/src/xpath/eval/descendantMemo.js +77 -0
  128. package/src/xpath/eval/documentOrder.js +142 -0
  129. package/src/xpath/eval/domNodes.js +174 -0
  130. package/src/xpath/eval/dynamicCall.js +27 -0
  131. package/src/xpath/eval/dynamicContext.js +161 -0
  132. package/src/xpath/eval/flow.js +115 -0
  133. package/src/xpath/eval/functionItems.js +194 -0
  134. package/src/xpath/eval/groupedChildren.js +88 -0
  135. package/src/xpath/eval/inline.js +71 -0
  136. package/src/xpath/eval/namespaceNodes.js +90 -0
  137. package/src/xpath/eval/nodeTests.js +164 -0
  138. package/src/xpath/eval/operators.js +190 -0
  139. package/src/xpath/eval/paths.js +163 -0
  140. package/src/xpath/eval/positional.js +81 -0
  141. package/src/xpath/eval/predicates.js +56 -0
  142. package/src/xpath/eval/primary.js +99 -0
  143. package/src/xpath/eval/resources.js +149 -0
  144. package/src/xpath/eval/scope.js +108 -0
  145. package/src/xpath/eval/sequenceType.js +193 -0
  146. package/src/xpath/eval/staticContext.js +142 -0
  147. package/src/xpath/eval/steps.js +98 -0
  148. package/src/xpath/eval/subtype.js +154 -0
  149. package/src/xpath/eval/typeExprs.js +147 -0
  150. package/src/xpath/eval/typeNames.js +101 -0
  151. package/src/xpath/eval/uris.js +21 -0
  152. package/src/xpath/eval/values.js +64 -0
  153. package/src/xpath/index.js +217 -0
  154. package/src/xpath/syntax/ast.js +130 -0
  155. package/src/xpath/syntax/chars.js +58 -0
  156. package/src/xpath/syntax/constructors.js +144 -0
  157. package/src/xpath/syntax/expressions.js +107 -0
  158. package/src/xpath/syntax/index.js +44 -0
  159. package/src/xpath/syntax/kindTests.js +134 -0
  160. package/src/xpath/syntax/lexer.js +220 -0
  161. package/src/xpath/syntax/operators.js +111 -0
  162. package/src/xpath/syntax/paths.js +102 -0
  163. package/src/xpath/syntax/postfix.js +91 -0
  164. package/src/xpath/syntax/primaries.js +149 -0
  165. package/src/xpath/syntax/steps.js +146 -0
  166. package/src/xpath/syntax/syntaxError.js +31 -0
  167. package/src/xpath/syntax/tokenStream.js +163 -0
  168. package/src/xpath/syntax/typeAst.js +76 -0
  169. package/src/xpath/syntax/typeOperators.js +127 -0
  170. package/src/xpath/syntax/types.js +158 -0
  171. package/src/xslt/api.js +153 -0
  172. package/src/xslt/compiler/acceptance.js +178 -0
  173. package/src/xslt/compiler/accumulatorDecl.js +146 -0
  174. package/src/xslt/compiler/attributeValues.js +174 -0
  175. package/src/xslt/compiler/attributes.js +211 -0
  176. package/src/xslt/compiler/avt.js +103 -0
  177. package/src/xslt/compiler/body.js +154 -0
  178. package/src/xslt/compiler/children.js +105 -0
  179. package/src/xslt/compiler/compatibility.js +130 -0
  180. package/src/xslt/compiler/components.js +179 -0
  181. package/src/xslt/compiler/contextItem.js +95 -0
  182. package/src/xslt/compiler/decimalFormats.js +132 -0
  183. package/src/xslt/compiler/declarationKinds.js +82 -0
  184. package/src/xslt/compiler/elementInfo.js +246 -0
  185. package/src/xslt/compiler/expressions.js +175 -0
  186. package/src/xslt/compiler/globalContextItem.js +92 -0
  187. package/src/xslt/compiler/globals.js +209 -0
  188. package/src/xslt/compiler/linker.js +134 -0
  189. package/src/xslt/compiler/miscDecl.js +171 -0
  190. package/src/xslt/compiler/modeDecl.js +88 -0
  191. package/src/xslt/compiler/moduleLoading.js +111 -0
  192. package/src/xslt/compiler/modules.js +158 -0
  193. package/src/xslt/compiler/outputDecl.js +209 -0
  194. package/src/xslt/compiler/overrideLinks.js +141 -0
  195. package/src/xslt/compiler/packageChecks.js +84 -0
  196. package/src/xslt/compiler/packageRegistry.js +95 -0
  197. package/src/xslt/compiler/packageTable.js +181 -0
  198. package/src/xslt/compiler/packageVersions.js +143 -0
  199. package/src/xslt/compiler/packages.js +138 -0
  200. package/src/xslt/compiler/registry.js +234 -0
  201. package/src/xslt/compiler/staticStage.js +197 -0
  202. package/src/xslt/compiler/staticVariables.js +93 -0
  203. package/src/xslt/compiler/stylesheet.js +205 -0
  204. package/src/xslt/compiler/tailPosition.js +74 -0
  205. package/src/xslt/compiler/templatesDecl.js +180 -0
  206. package/src/xslt/compiler/useAccumulators.js +76 -0
  207. package/src/xslt/compiler/useWhen.js +94 -0
  208. package/src/xslt/compiler/visibility.js +160 -0
  209. package/src/xslt/index.js +11 -0
  210. package/src/xslt/instructions/analyzeString.js +137 -0
  211. package/src/xslt/instructions/assert.js +72 -0
  212. package/src/xslt/instructions/attributeSets.js +115 -0
  213. package/src/xslt/instructions/conditional.js +99 -0
  214. package/src/xslt/instructions/control.js +123 -0
  215. package/src/xslt/instructions/copy.js +151 -0
  216. package/src/xslt/instructions/elements.js +161 -0
  217. package/src/xslt/instructions/evaluate.js +189 -0
  218. package/src/xslt/instructions/fork.js +35 -0
  219. package/src/xslt/instructions/grouping.js +106 -0
  220. package/src/xslt/instructions/index.js +94 -0
  221. package/src/xslt/instructions/iterate.js +182 -0
  222. package/src/xslt/instructions/literal.js +142 -0
  223. package/src/xslt/instructions/maps.js +72 -0
  224. package/src/xslt/instructions/merge.js +190 -0
  225. package/src/xslt/instructions/number.js +205 -0
  226. package/src/xslt/instructions/output.js +102 -0
  227. package/src/xslt/instructions/params.js +110 -0
  228. package/src/xslt/instructions/resultDocument.js +143 -0
  229. package/src/xslt/instructions/sort.js +114 -0
  230. package/src/xslt/instructions/sourceDocument.js +73 -0
  231. package/src/xslt/instructions/templates.js +153 -0
  232. package/src/xslt/instructions/text.js +151 -0
  233. package/src/xslt/instructions/try.js +186 -0
  234. package/src/xslt/instructions/variables.js +119 -0
  235. package/src/xslt/names.js +204 -0
  236. package/src/xslt/patterns/axisOrigins.js +121 -0
  237. package/src/xslt/patterns/compile.js +151 -0
  238. package/src/xslt/patterns/general.js +132 -0
  239. package/src/xslt/patterns/grammar.js +190 -0
  240. package/src/xslt/patterns/modes.js +153 -0
  241. package/src/xslt/patterns/paths.js +130 -0
  242. package/src/xslt/patterns/patternChecks.js +29 -0
  243. package/src/xslt/patterns/steps.js +162 -0
  244. package/src/xslt/processor.js +178 -0
  245. package/src/xslt/runtime/accumulatorFunctions.js +48 -0
  246. package/src/xslt/runtime/accumulators.js +174 -0
  247. package/src/xslt/runtime/apply.js +153 -0
  248. package/src/xslt/runtime/atomicKeys.js +36 -0
  249. package/src/xslt/runtime/availability.js +162 -0
  250. package/src/xslt/runtime/baseUri.js +64 -0
  251. package/src/xslt/runtime/compilerHook.js +27 -0
  252. package/src/xslt/runtime/conditional.js +110 -0
  253. package/src/xslt/runtime/context.js +106 -0
  254. package/src/xslt/runtime/copy.js +123 -0
  255. package/src/xslt/runtime/copyFunctions.js +71 -0
  256. package/src/xslt/runtime/copyOrigins.js +60 -0
  257. package/src/xslt/runtime/defaultMethod.js +42 -0
  258. package/src/xslt/runtime/documentContent.js +93 -0
  259. package/src/xslt/runtime/dynamicXPath.js +90 -0
  260. package/src/xslt/runtime/functionCache.js +65 -0
  261. package/src/xslt/runtime/functionCall.js +57 -0
  262. package/src/xslt/runtime/functions.js +191 -0
  263. package/src/xslt/runtime/groups.js +108 -0
  264. package/src/xslt/runtime/invocation.js +156 -0
  265. package/src/xslt/runtime/iteration.js +107 -0
  266. package/src/xslt/runtime/keys.js +141 -0
  267. package/src/xslt/runtime/lazy.js +97 -0
  268. package/src/xslt/runtime/locations.js +86 -0
  269. package/src/xslt/runtime/machine.js +137 -0
  270. package/src/xslt/runtime/merging.js +167 -0
  271. package/src/xslt/runtime/namespaceScope.js +141 -0
  272. package/src/xslt/runtime/numberFormat.js +89 -0
  273. package/src/xslt/runtime/numbering.js +168 -0
  274. package/src/xslt/runtime/params.js +59 -0
  275. package/src/xslt/runtime/properties.js +103 -0
  276. package/src/xslt/runtime/rawResults.js +82 -0
  277. package/src/xslt/runtime/sequenceReceiver.js +102 -0
  278. package/src/xslt/runtime/snapshot.js +66 -0
  279. package/src/xslt/runtime/sortKeys.js +119 -0
  280. package/src/xslt/runtime/streamAvailable.js +60 -0
  281. package/src/xslt/runtime/strip.js +102 -0
  282. package/src/xslt/runtime/transformFunction.js +169 -0
  283. package/src/xslt/runtime/transformation.js +168 -0
  284. package/src/xslt/runtime/transformationContext.js +85 -0
  285. package/src/xslt/runtime/treeReceiver.js +194 -0
  286. 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
+ ];