@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,141 @@
1
+ /**
2
+ * The namespaces of an element under construction: its own namespace
3
+ * nodes (from its name, its attributes' names and namespace
4
+ * instructions) and those in scope from its ancestors, with the `xmlns`
5
+ * declarations the DOM needs (XSLT 3.0 section 5.7.3, namespace fixup).
6
+ *
7
+ * @module @tradik/xslt3/xslt/runtime/namespaceScope
8
+ */
9
+
10
+ import { XML_NS, XMLNS_NS } from "../names.js";
11
+
12
+ /**
13
+ * @param {string} qname
14
+ * @returns {string} the prefix of a lexical QName, "" for none
15
+ */
16
+ export const prefixOf = (qname) => {
17
+ const colon = qname.indexOf(":");
18
+ return colon < 0 ? "" : qname.slice(0, colon);
19
+ };
20
+
21
+ /** Namespace bookkeeping of the element a receiver builds. */
22
+ export class NamespaceScope {
23
+ /**
24
+ * @param {Node} parent - The element (or document) built
25
+ * @param {Map<string, string>} namespaces - In scope at its parent
26
+ */
27
+ constructor(parent, namespaces) {
28
+ this.parent = parent;
29
+ this.inherited = namespaces;
30
+ this.namespaces = namespaces;
31
+ this.ownNamespaces = false;
32
+ /** @type {Map<string, string>|null} namespace nodes of this element */
33
+ this.own = null;
34
+ /** @type {Set<string>} prefixes of namespace nodes added explicitly */
35
+ this.explicit = new Set();
36
+ /** @type {((old: Element, element: Element) => void)|null} */
37
+ this.onReplace = null;
38
+ }
39
+
40
+ /**
41
+ * Gives the element a namespace node that conflicts with the prefix of
42
+ * its name: the element is renamed with a new prefix (namespace fixup).
43
+ * @param {string} prefix
44
+ * @param {string} uri
45
+ * @returns {boolean} false when the conflict is not with the name, or
46
+ * when the name's namespace node was itself added explicitly
47
+ */
48
+ rebind(prefix, uri) {
49
+ const old = this.parent;
50
+ if (prefix === "" || old.prefix !== prefix || this.explicit.has(prefix)) {
51
+ return false;
52
+ }
53
+ let fresh;
54
+ for (let i = 0; this.namespaces.has((fresh = `${prefix}_${i}`)); i++);
55
+ const element = old.ownerDocument.createElementNS(
56
+ old.namespaceURI,
57
+ `${fresh}:${old.localName}`,
58
+ );
59
+ for (const attribute of old.attributes) {
60
+ if (attribute.name === `xmlns:${prefix}`) continue;
61
+ element.setAttributeNS(
62
+ attribute.namespaceURI,
63
+ attribute.name,
64
+ attribute.value,
65
+ );
66
+ }
67
+ old.parentNode?.replaceChild(element, old);
68
+ this.onReplace?.(old, element);
69
+ this.parent = element;
70
+ this.own.delete(prefix);
71
+ if (this.ownNamespaces) {
72
+ const before = this.inherited.get(prefix);
73
+ if (before === undefined) this.namespaces.delete(prefix);
74
+ else this.namespaces.set(prefix, before);
75
+ }
76
+ this.declare(fresh, old.namespaceURI);
77
+ return this.declare(prefix, uri);
78
+ }
79
+
80
+ /**
81
+ * Gives the element a namespace node, declared when not in scope.
82
+ * @param {string} prefix
83
+ * @param {string} uri
84
+ * @returns {boolean} false when the element already binds the prefix to
85
+ * another URI
86
+ */
87
+ declare(prefix, uri) {
88
+ if (prefix === "xml") return uri === XML_NS;
89
+ const own = this.own?.get(prefix);
90
+ if (own !== undefined) return own === uri;
91
+ if (prefix !== "" && uri === "") return true;
92
+ if (!this.own) this.own = new Map();
93
+ this.own.set(prefix, uri);
94
+ if ((this.namespaces.get(prefix) ?? "") !== uri) {
95
+ if (!this.ownNamespaces) {
96
+ this.namespaces = new Map(this.namespaces);
97
+ this.ownNamespaces = true;
98
+ }
99
+ this.namespaces.set(prefix, uri);
100
+ const name = prefix === "" ? "xmlns" : `xmlns:${prefix}`;
101
+ this.parent.setAttributeNS(XMLNS_NS, name, uri);
102
+ }
103
+ return true;
104
+ }
105
+
106
+ /**
107
+ * Gives the element a namespace node that it must keep (copied by a
108
+ * literal result element or xsl:copy, or built by xsl:namespace): two
109
+ * such nodes for one prefix conflict (XTDE0430), rather than causing a
110
+ * namespace fixup.
111
+ * @param {string} prefix
112
+ * @param {string} uri
113
+ * @returns {boolean} as {@link NamespaceScope#declare}
114
+ */
115
+ declareExplicit(prefix, uri) {
116
+ const declared = this.declare(prefix, uri);
117
+ if (declared) this.explicit.add(prefix);
118
+ return declared;
119
+ }
120
+
121
+ /**
122
+ * A prefix bound to a URI on the element, declared when needed: one in
123
+ * scope, else `base_1`, `base_2`... (`ns0`, `ns1`... without base).
124
+ * @param {string} uri
125
+ * @param {string} base - Preferred prefix, "" for none
126
+ * @returns {string}
127
+ */
128
+ prefixFor(uri, base) {
129
+ for (const [prefix, bound] of this.namespaces) {
130
+ if (bound === uri && prefix !== "" && this.declare(prefix, uri)) {
131
+ return prefix;
132
+ }
133
+ }
134
+ for (let i = base ? 1 : 0; ; i++) {
135
+ const prefix = base ? `${base}_${i}` : `ns${i}`;
136
+ if (!this.namespaces.has(prefix) && this.declare(prefix, uri)) {
137
+ return prefix;
138
+ }
139
+ }
140
+ }
141
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The format string of xsl:number (XSLT 3.0 section 12.4): alphanumeric
3
+ * format tokens separated by punctuation, with a prefix and a suffix,
4
+ * applied to a list of numbers.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/numberFormat
7
+ */
8
+
9
+ import { formatToken } from "../../functions/format/formatInteger.js";
10
+ import { numberLanguage } from "../../functions/format/numberLanguages.js";
11
+
12
+ const ALPHANUMERIC = /[\p{L}\p{N}]/u;
13
+
14
+ /**
15
+ * Splits a format string into a prefix, tokens with their separators,
16
+ * and a suffix.
17
+ * @param {string} format
18
+ * @returns {{prefix: string, tokens: string[], separators: string[], suffix: string}}
19
+ */
20
+ export function parseFormat(format) {
21
+ const parts = [];
22
+ let current = "";
23
+ let alnum = null;
24
+ for (const c of format) {
25
+ const isAlnum = ALPHANUMERIC.test(c);
26
+ if (alnum !== null && isAlnum !== alnum) {
27
+ parts.push({ text: current, alnum });
28
+ current = "";
29
+ }
30
+ current += c;
31
+ alnum = isAlnum;
32
+ }
33
+ if (current) parts.push({ text: current, alnum });
34
+ let prefix = "";
35
+ let suffix = "";
36
+ if (parts[0] && !parts[0].alnum) prefix = parts.shift().text;
37
+ if (parts.length && !parts.at(-1).alnum) suffix = parts.pop().text;
38
+ const tokens = parts.filter((p) => p.alnum).map((p) => p.text);
39
+ const separators = parts.filter((p) => !p.alnum).map((p) => p.text);
40
+ if (tokens.length === 0) {
41
+ // no format token: "1", with the punctuation as prefix and suffix
42
+ return { prefix, tokens: ["1"], separators: [], suffix: prefix };
43
+ }
44
+ return { prefix, tokens, separators, suffix };
45
+ }
46
+
47
+ /**
48
+ * Inserts a grouping separator every `size` digits.
49
+ * @param {string} digits
50
+ * @param {string} separator
51
+ * @param {number} size
52
+ * @returns {string}
53
+ */
54
+ function group(digits, separator, size) {
55
+ if (!separator || !(size > 0) || !/^\p{Nd}+$/u.test(digits)) return digits;
56
+ const chars = [...digits];
57
+ let result = "";
58
+ chars.forEach((c, i) => {
59
+ if (i > 0 && (chars.length - i) % size === 0) result += separator;
60
+ result += c;
61
+ });
62
+ return result;
63
+ }
64
+
65
+ /**
66
+ * Formats a list of numbers.
67
+ * @param {bigint[]} numbers
68
+ * @param {string} format
69
+ * @param {{ordinal: boolean|string, separator: string, size: number,
70
+ * lang?: string|null}} options - ordinal: false, or the value of the
71
+ * ordinal attribute
72
+ * @returns {string}
73
+ */
74
+ export function formatNumbers(numbers, format, options) {
75
+ const { prefix, tokens, separators, suffix } = parseFormat(format);
76
+ const language = numberLanguage(options.lang);
77
+ let result = prefix;
78
+ numbers.forEach((n, i) => {
79
+ if (i > 0) {
80
+ result += separators[Math.min(i, separators.length) - 1] ?? ".";
81
+ }
82
+ const token = tokens[Math.min(i, tokens.length - 1)];
83
+ // a negative number (start-at below 1) gets a minus sign
84
+ const text = formatToken(n < 0n ? -n : n, token, options.ordinal, language);
85
+ result +=
86
+ (n < 0n ? "-" : "") + group(text, options.separator, options.size);
87
+ });
88
+ return result + suffix;
89
+ }
@@ -0,0 +1,168 @@
1
+ /**
2
+ * The place of a node in a document as xsl:number counts it (XSLT 3.0
3
+ * section 12.2): levels single, multiple and any, with count and from
4
+ * patterns.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/numbering
7
+ */
8
+
9
+ import {
10
+ nodeLocalName,
11
+ nodeNamespace,
12
+ parentOf,
13
+ } from "../../xpath/eval/domNodes.js";
14
+
15
+ /**
16
+ * The default count pattern: nodes of the same kind and name.
17
+ * @param {Node} node
18
+ * @returns {(other: Node) => boolean}
19
+ */
20
+ export function sameKindAndName(node) {
21
+ const type = node.nodeType === 4 ? 3 : node.nodeType;
22
+ const local = nodeLocalName(node);
23
+ const uri = nodeNamespace(node);
24
+ return (other) => {
25
+ const otherType = other.nodeType === 4 ? 3 : other.nodeType;
26
+ return (
27
+ otherType === type &&
28
+ nodeLocalName(other) === local &&
29
+ nodeNamespace(other) === uri
30
+ );
31
+ };
32
+ }
33
+
34
+ /**
35
+ * The key of the default count pattern of a node (see sameKindAndName):
36
+ * numbers memoized for one key are not valid for another.
37
+ * @param {Node} node
38
+ * @returns {string}
39
+ */
40
+ export function countKey(node) {
41
+ const type = node.nodeType === 4 ? 3 : node.nodeType;
42
+ return `${type}|${nodeNamespace(node) ?? ""}|${nodeLocalName(node)}`;
43
+ }
44
+
45
+ /**
46
+ * The memo of an xsl:number instruction in a transformation: the numbers
47
+ * already given to nodes, so that numbering a long list stays linear.
48
+ * Only valid while counting depends on nothing but the tree: the caller
49
+ * does not memoize patterns that read variables.
50
+ * @param {object} tx - The transformation (holds the memos)
51
+ * @param {object} instruction - Identity of the instruction
52
+ * @param {string} key - The count pattern ("" for an explicit one, else
53
+ * the {@link countKey} of the numbered node)
54
+ * @returns {WeakMap<Node, number>}
55
+ */
56
+ export function numberMemo(tx, instruction, key) {
57
+ tx.numberMemos ??= new WeakMap();
58
+ let byKey = tx.numberMemos.get(instruction);
59
+ if (!byKey) tx.numberMemos.set(instruction, (byKey = new Map()));
60
+ let memo = byKey.get(key);
61
+ if (!memo) byKey.set(key, (memo = new WeakMap()));
62
+ return memo;
63
+ }
64
+
65
+ /**
66
+ * Number of preceding siblings that match, plus one; the walk stops at a
67
+ * sibling whose number is memoized (a counted node: its number includes
68
+ * itself and the counted siblings before it).
69
+ * @param {Node} node - A counted node
70
+ * @param {(node: Node) => boolean} count
71
+ * @param {WeakMap<Node, number>|null} memo
72
+ * @returns {number}
73
+ */
74
+ function siblingNumber(node, count, memo) {
75
+ if (node.nodeType === 2 || node.nodeType === 13) return 1;
76
+ const own = memo?.get(node);
77
+ if (own !== undefined) return own;
78
+ let n = 1;
79
+ for (let s = node.previousSibling; s; s = s.previousSibling) {
80
+ const known = memo?.get(s);
81
+ if (known !== undefined) {
82
+ n += known;
83
+ break;
84
+ }
85
+ if (s.nodeType !== 10 && count(s)) n++;
86
+ }
87
+ memo?.set(node, n);
88
+ return n;
89
+ }
90
+
91
+ /**
92
+ * Ancestors-or-self of a node, innermost first, up to (including) the
93
+ * first one matching `from` (all of them when none matches).
94
+ * @param {Node} node
95
+ * @param {((node: Node) => boolean)|null} from
96
+ * @returns {Node[]}
97
+ */
98
+ function ancestorsBelowFrom(node, from) {
99
+ const result = [];
100
+ for (let a = node; a; a = parentOf(a)) {
101
+ result.push(a);
102
+ if (from && from(a)) break;
103
+ }
104
+ return result;
105
+ }
106
+
107
+ /**
108
+ * level="single" and level="multiple".
109
+ * @param {Node} node
110
+ * @param {(node: Node) => boolean} count
111
+ * @param {((node: Node) => boolean)|null} from
112
+ * @param {boolean} multiple
113
+ * @param {WeakMap<Node, number>|null} [memo] - Sibling numbers already
114
+ * computed with the same count pattern (see numberMemo)
115
+ * @returns {number[]}
116
+ */
117
+ export function numberHierarchy(node, count, from, multiple, memo = null) {
118
+ const numbers = [];
119
+ for (const a of ancestorsBelowFrom(node, from)) {
120
+ if (!count(a)) continue;
121
+ numbers.unshift(siblingNumber(a, count, memo));
122
+ if (!multiple) break;
123
+ }
124
+ return numbers;
125
+ }
126
+
127
+ /**
128
+ * The node before another in document order (preceding or ancestor).
129
+ * @param {Node} node
130
+ * @returns {Node|null}
131
+ */
132
+ function previousInDocument(node) {
133
+ if (node.nodeType === 2 || node.nodeType === 13) return parentOf(node);
134
+ const sibling = node.previousSibling;
135
+ if (!sibling) return parentOf(node);
136
+ let last = sibling;
137
+ while (last.lastChild) last = last.lastChild;
138
+ return last;
139
+ }
140
+
141
+ /**
142
+ * level="any": the counted nodes met walking back in document order from
143
+ * the node to a `from` node or the root. The walk from a node is the walk
144
+ * from any node it meets, so it stops at a node whose total is memoized:
145
+ * numbering in document order (for-each, apply-templates) only walks back
146
+ * to the node numbered before, and is linear instead of quadratic. In
147
+ * other orders nothing memoized is met and the walk is complete.
148
+ * @param {Node} node
149
+ * @param {(node: Node) => boolean} count
150
+ * @param {((node: Node) => boolean)|null} from
151
+ * @param {WeakMap<Node, number>|null} [memo] - Totals already computed
152
+ * with the same count and from patterns (see numberMemo)
153
+ * @returns {number[]}
154
+ */
155
+ export function numberAny(node, count, from, memo = null) {
156
+ let n = 0;
157
+ for (let p = node; p; p = previousInDocument(p)) {
158
+ const known = memo?.get(p);
159
+ if (known !== undefined) {
160
+ n += known;
161
+ break;
162
+ }
163
+ if (p.nodeType !== 10 && count(p)) n++;
164
+ if (from && from(p)) break;
165
+ }
166
+ memo?.set(node, n);
167
+ return n === 0 ? [] : [n];
168
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Names and values given to the API: parameters by name, template and
3
+ * function names.
4
+ *
5
+ * @module @tradik/xslt3/xslt/runtime/params
6
+ */
7
+
8
+ import { toSequence } from "../../xpath/eval/values.js";
9
+ import { AtomicValue } from "../../xdm/atomic.js";
10
+ import { types } from "../../xdm/types.js";
11
+
12
+ /**
13
+ * The Clark name of a name given to the API: `local` (no namespace),
14
+ * `Q{uri}local` or `{uri}local`.
15
+ * @param {string} name
16
+ * @returns {string} `{uri}local`
17
+ */
18
+ export function clarkName(name) {
19
+ if (name.startsWith("Q{")) return name.slice(1);
20
+ return name.startsWith("{") ? name : `{}${name}`;
21
+ }
22
+
23
+ /**
24
+ * An xs:untypedAtomic value, for parameters that should convert to the
25
+ * declared type of the parameter (as a string from a command line does)
26
+ * instead of being checked as an xs:string.
27
+ * @param {*} value - Converted to a string
28
+ * @returns {AtomicValue}
29
+ */
30
+ export const untypedAtomic = (value) =>
31
+ new AtomicValue(types.untypedAtomic, String(value));
32
+
33
+ /**
34
+ * A parameter value whose JavaScript strings become xs:untypedAtomic.
35
+ * @param {*} value
36
+ * @returns {*}
37
+ */
38
+ const untypedStrings = (value) => {
39
+ if (typeof value === "string") return untypedAtomic(value);
40
+ return Array.isArray(value) ? value.map(untypedStrings) : value;
41
+ };
42
+
43
+ /**
44
+ * Normalizes parameters given by name to Clark names with XDM values
45
+ * (JavaScript values are converted, see xpath/eval/values.js).
46
+ * @param {object|Map|undefined} params
47
+ * @param {boolean} [asUntyped] - JavaScript strings are xs:untypedAtomic
48
+ * @returns {Map<string, Array>}
49
+ */
50
+ export function normalizeParams(params, asUntyped = false) {
51
+ const result = new Map();
52
+ const entries =
53
+ params instanceof Map ? [...params] : Object.entries(params ?? {});
54
+ for (const [name, value] of entries) {
55
+ const converted = asUntyped ? untypedStrings(value) : value;
56
+ result.set(clarkName(name), toSequence(converted));
57
+ }
58
+ return result;
59
+ }
@@ -0,0 +1,103 @@
1
+ /**
2
+ * What the processor reports about itself: system-property() values
3
+ * (XSLT 3.0 section 20.4.4) and the instructions element-available()
4
+ * knows.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/properties
7
+ */
8
+
9
+ /** System properties in the XSLT namespace. */
10
+ const PROPERTIES = {
11
+ version: "3.0",
12
+ vendor: "tradik",
13
+ "vendor-url": "https://xslt-processor.tradik.com/",
14
+ "product-name": "@tradik/xslt3",
15
+ "product-version": "0.0.0",
16
+ "is-schema-aware": "no",
17
+ "supports-serialization": "yes",
18
+ "supports-backwards-compatibility": "yes",
19
+ "supports-namespace-axis": "yes",
20
+ "supports-streaming": "no",
21
+ "supports-dynamic-evaluation": "yes",
22
+ "supports-higher-order-functions": "yes",
23
+ "xpath-version": "3.1",
24
+ "xsd-version": "1.1",
25
+ };
26
+
27
+ /**
28
+ * The value of a system property in the XSLT namespace.
29
+ * @param {string} local - Local name
30
+ * @returns {string} "" for an unknown property
31
+ */
32
+ export const systemProperty = (local) => PROPERTIES[local] ?? "";
33
+
34
+ /** @returns {string[]} the local names of the system properties */
35
+ export const systemPropertyNames = () => Object.keys(PROPERTIES);
36
+
37
+ /** Local names of the XSLT elements (instructions and declarations) implemented. */
38
+ export const availableInstructions = new Set([
39
+ "attribute-set",
40
+ "character-map",
41
+ "decimal-format",
42
+ "function",
43
+ "import",
44
+ "include",
45
+ "key",
46
+ "mode",
47
+ "namespace-alias",
48
+ "output",
49
+ "param",
50
+ "preserve-space",
51
+ "strip-space",
52
+ "stylesheet",
53
+ "template",
54
+ "transform",
55
+ "analyze-string",
56
+ "apply-imports",
57
+ "apply-templates",
58
+ "attribute",
59
+ "call-template",
60
+ "choose",
61
+ "comment",
62
+ "copy",
63
+ "copy-of",
64
+ "document",
65
+ "element",
66
+ "fallback",
67
+ "for-each",
68
+ "for-each-group",
69
+ "if",
70
+ "message",
71
+ "namespace",
72
+ "next-match",
73
+ "number",
74
+ "perform-sort",
75
+ "processing-instruction",
76
+ "result-document",
77
+ "sequence",
78
+ "text",
79
+ "value-of",
80
+ "try",
81
+ "catch",
82
+ "map",
83
+ "map-entry",
84
+ "variable",
85
+ // XSLT 3.0 instructions of task 0031
86
+ "accumulator",
87
+ "accumulator-rule",
88
+ "assert",
89
+ "break",
90
+ "evaluate",
91
+ "fork",
92
+ "iterate",
93
+ "merge",
94
+ "merge-action",
95
+ "merge-key",
96
+ "merge-source",
97
+ "next-iteration",
98
+ "on-completion",
99
+ "on-empty",
100
+ "on-non-empty",
101
+ "source-document",
102
+ "where-populated",
103
+ ]);
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Results of a transformation (XSLT 3.0 section 2.3.6, build-tree in
3
+ * Serialization 3.1 section 3): a result whose serialization parameters
4
+ * have build-tree "no" (the default of the json and adaptive methods) is
5
+ * the raw sequence the sequence constructor delivers, so that maps,
6
+ * arrays and atomic values reach the serializer as they are. Otherwise a
7
+ * result tree is built from it by sequence normalization: with an
8
+ * item-separator, the separator goes between all items.
9
+ *
10
+ * @module @tradik/xslt3/xslt/runtime/rawResults
11
+ */
12
+
13
+ import { normalizeSequence } from "../../serialize/sequence.js";
14
+ import { SequenceReceiver } from "./sequenceReceiver.js";
15
+ import { TreeReceiver } from "./treeReceiver.js";
16
+
17
+ /** Methods whose results are raw unless build-tree says otherwise. */
18
+ const RAW_METHODS = new Set(["json", "adaptive"]);
19
+
20
+ /**
21
+ * Whether a result with these serialization parameters is a tree.
22
+ * @param {object} [output] - Serialization parameters (spec names)
23
+ * @returns {boolean}
24
+ */
25
+ export function buildsTree(output) {
26
+ const value = output?.["build-tree"];
27
+ if (typeof value === "boolean") return value;
28
+ if (typeof value === "string") return /^\s*(yes|true|1)\s*$/.test(value);
29
+ return !RAW_METHODS.has(output?.method);
30
+ }
31
+
32
+ /**
33
+ * A result tree from a raw sequence, normalized with an item separator.
34
+ * Nodes are copied, as the sequence may hold nodes of other trees.
35
+ * @param {Array} items
36
+ * @param {string} separator
37
+ * @param {DocumentFragment} fragment - Receives the tree
38
+ * @returns {DocumentFragment}
39
+ */
40
+ function normalizedTree(items, separator, fragment) {
41
+ const document = fragment.ownerDocument;
42
+ for (const entry of normalizeSequence(items, separator)) {
43
+ fragment.appendChild(
44
+ typeof entry === "string"
45
+ ? document.createTextNode(entry)
46
+ : document.importNode(entry, true),
47
+ );
48
+ }
49
+ return fragment;
50
+ }
51
+
52
+ /**
53
+ * The receiver of a result: a tree under a new document fragment, or a
54
+ * sequence for a raw result.
55
+ * @param {object} output - Serialization parameters
56
+ * @param {() => Document} owner - Owner document of the result's nodes
57
+ * @param {boolean} [buildTree] - Overrides build-tree (the `buildTree`
58
+ * option of the transformation)
59
+ * @returns {{receiver: object, value: () => (DocumentFragment|Array)}}
60
+ * the receiver and, once the result is complete, the fragment or the
61
+ * sequence
62
+ */
63
+ export function resultReceiver(output, owner, buildTree) {
64
+ const separator = output?.["item-separator"];
65
+ const tree = buildTree ?? buildsTree(output);
66
+ if (!tree || separator !== undefined) {
67
+ const receiver = new SequenceReceiver(owner);
68
+ const value = tree
69
+ ? () =>
70
+ normalizedTree(
71
+ receiver.items,
72
+ separator,
73
+ owner().createDocumentFragment(),
74
+ )
75
+ : () => receiver.items;
76
+ return { receiver, value };
77
+ }
78
+ const fragment = owner().createDocumentFragment();
79
+ const receiver = new TreeReceiver(fragment, new Map());
80
+ receiver.resultTree = true;
81
+ return { receiver, value: () => fragment };
82
+ }