@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,65 @@
1
+ /**
2
+ * Memoized stylesheet functions (XSLT 3.0 section 10.3.2, cache="yes" and
3
+ * new-each-time="no"): a call with the same arguments as an earlier one
4
+ * of the transformation returns the earlier result. Atomic values are
5
+ * compared by type and value, nodes and other items by identity.
6
+ *
7
+ * @module @tradik/xslt3/xslt/runtime/functionCache
8
+ */
9
+
10
+ import { isAtomic } from "../../xdm/atomic.js";
11
+ import { canonicalString } from "../../xdm/lexical.js";
12
+
13
+ /**
14
+ * The value of an atomic item as a string: canonical, except for QNames
15
+ * (their namespace, not their prefix).
16
+ * @param {import("../../xdm/atomic.js").AtomicValue} item
17
+ * @returns {string}
18
+ */
19
+ function valueKey(item) {
20
+ const { namespaceURI, localName } = item.value ?? {};
21
+ return localName === undefined
22
+ ? canonicalString(item)
23
+ : `{${namespaceURI}}${localName}`;
24
+ }
25
+
26
+ /**
27
+ * The key of a list of arguments.
28
+ * @param {Array<Array>} args - Converted argument values
29
+ * @param {object} tx - The transformation (numbers the other items)
30
+ * @returns {string}
31
+ */
32
+ function argumentsKey(args, tx) {
33
+ tx.itemIds ??= { ids: new WeakMap(), next: 0 };
34
+ const { ids } = tx.itemIds;
35
+ const idOf = (item) => {
36
+ if (!ids.has(item)) ids.set(item, tx.itemIds.next++);
37
+ return ids.get(item);
38
+ };
39
+ return JSON.stringify(
40
+ args.map((value) =>
41
+ value.map((item) =>
42
+ isAtomic(item) ? [item.type.name, valueKey(item)] : idOf(item),
43
+ ),
44
+ ),
45
+ );
46
+ }
47
+
48
+ /**
49
+ * Calls a function through its cache.
50
+ * @param {object} compiled - The compiled function (its cache key)
51
+ * @param {Array<Array>} args - Converted argument values
52
+ * @param {object} tx - The transformation
53
+ * @param {() => Array} call - Computes the result
54
+ * @returns {Array}
55
+ */
56
+ export function cachedCall(compiled, args, tx, call) {
57
+ tx.functionCaches ??= new Map();
58
+ let cache = tx.functionCaches.get(compiled);
59
+ if (!cache) tx.functionCaches.set(compiled, (cache = new Map()));
60
+ const key = argumentsKey(args, tx);
61
+ if (cache.has(key)) return cache.get(key);
62
+ const result = call();
63
+ cache.set(key, result);
64
+ return result;
65
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Calls of stylesheet functions (xsl:function) from XPath: the arguments
3
+ * converted to the parameter types and bound, the body run with an
4
+ * absent focus, the result converted to the declared type.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/functionCall
7
+ */
8
+
9
+ import { cachedCall } from "./functionCache.js";
10
+ import { SequenceReceiver } from "./sequenceReceiver.js";
11
+
12
+ /**
13
+ * Calls a compiled stylesheet function.
14
+ * @param {{params: object[], body: Array, convert: Function,
15
+ * memo?: boolean}} compiled
16
+ * @param {Array<Array>} args - Argument values
17
+ * @param {object} context - XPath dynamic context of the call
18
+ * @returns {Array} the result
19
+ */
20
+ export function callFunction(compiled, args, context) {
21
+ const { tx } = context.xc;
22
+ const values = compiled.params.map((param, i) => param.convert(args[i]));
23
+ if (compiled.memo) {
24
+ return cachedCall(compiled, values, tx, () => run(compiled, values, tx));
25
+ }
26
+ return run(compiled, values, tx);
27
+ }
28
+
29
+ /**
30
+ * Runs the body of a stylesheet function.
31
+ * @param {object} compiled
32
+ * @param {Array<Array>} values - Converted argument values
33
+ * @param {object} tx - The transformation
34
+ * @returns {Array} the result
35
+ */
36
+ function run(compiled, values, tx) {
37
+ let env = tx.globalEnv;
38
+ for (const value of values) env = { value, next: env };
39
+ const xc = {
40
+ tx,
41
+ item: undefined,
42
+ position: 0,
43
+ size: 0,
44
+ env,
45
+ mode: tx.defaultMode,
46
+ rule: null,
47
+ group: undefined,
48
+ groupKey: undefined,
49
+ regex: undefined,
50
+ tunnel: null,
51
+ temporary: true,
52
+ dyn: null,
53
+ };
54
+ const out = new SequenceReceiver(tx.scratch);
55
+ tx.machine.runBody(compiled.body, xc, out);
56
+ return compiled.convert(out.items);
57
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * The functions XSLT adds to XPath (XSLT 3.0 section 20): current(),
3
+ * current-group(), current-grouping-key(), regex-group(), key(),
4
+ * document() and the unparsed entity functions, with the availability
5
+ * functions (availability.js) and copy-of()/snapshot() (copyFunctions.js).
6
+ * They read the XSLT context from the XPath dynamic context
7
+ * (`context.xc`, see context.js).
8
+ *
9
+ * @module @tradik/xslt3/xslt/runtime/functions
10
+ */
11
+
12
+ import { stringItem } from "../../xpath/eval/atomics.js";
13
+ import { rootOf } from "../../xpath/eval/domNodes.js";
14
+ import { resolveUri } from "../../xpath/eval/uris.js";
15
+ import { AtomicValue, isNode } from "../../xdm/atomic.js";
16
+ import { types } from "../../xdm/types.js";
17
+ import { stringValue } from "../../xdm/nodes.js";
18
+ import { xsltError } from "../names.js";
19
+ import { accumulatorFunctions } from "./accumulatorFunctions.js";
20
+ import { availabilityFunctions, nameArgument } from "./availability.js";
21
+ import { copyFunctions } from "./copyFunctions.js";
22
+ import { keyLookup } from "./keys.js";
23
+ import { mergeFunctions } from "./merging.js";
24
+ import { streamAvailableFunctions } from "./streamAvailable.js";
25
+ import { transformFunctions } from "./transformFunction.js";
26
+
27
+ /**
28
+ * Whether a node is a node of the subtree rooted at another.
29
+ * @param {Node} node
30
+ * @param {Node} top
31
+ * @returns {boolean}
32
+ */
33
+ function within(node, top) {
34
+ for (let a = node; a; a = a.parentNode ?? a.ownerElement) {
35
+ if (a === top) return true;
36
+ }
37
+ return false;
38
+ }
39
+
40
+ /**
41
+ * key().
42
+ * @param {Array} args
43
+ * @param {object} context
44
+ * @returns {Array}
45
+ */
46
+ function key([[name], values, top], context) {
47
+ const { uri, local } = nameArgument(name.value, context, "XTDE1260");
48
+ let root = top?.[0];
49
+ if (!top) {
50
+ if (!isNode(context.contextItem)) {
51
+ throw xsltError("XTDE1270", "key() needs a context node");
52
+ }
53
+ root = rootOf(context.contextItem);
54
+ if (root.nodeType !== 9 && root.nodeType !== 11) {
55
+ throw xsltError("XTDE1270", "key() needs a node in a document");
56
+ }
57
+ }
58
+ const nodes = keyLookup(
59
+ context.xc,
60
+ `{${uri}}${local}`,
61
+ values,
62
+ rootOf(root),
63
+ context.sc?.owner,
64
+ );
65
+ return root === rootOf(root) ? nodes : nodes.filter((n) => within(n, root));
66
+ }
67
+
68
+ /**
69
+ * document().
70
+ * @param {Array} args
71
+ * @param {object} context
72
+ * @returns {Array}
73
+ */
74
+ function documentFunction([items, base], context) {
75
+ const results = [];
76
+ for (const item of items) {
77
+ const fixedBase = base?.length
78
+ ? (rootOf(base[0]).documentURI ?? context.staticBaseUri)
79
+ : null;
80
+ const baseUri =
81
+ fixedBase ??
82
+ (isNode(item)
83
+ ? (rootOf(item).documentURI ?? context.staticBaseUri)
84
+ : context.staticBaseUri);
85
+ const uri = resolveUri(stringValue(item).replace(/#.*$/, ""), baseUri);
86
+ const own = context.xc.tx.stylesheet.moduleDocuments.get(uri);
87
+ results.push(own ?? context.loadDocument(uri));
88
+ }
89
+ return context.order.sort(results);
90
+ }
91
+
92
+ /** XSLT functions, as function library definitions. */
93
+ export const xsltFunctions = [
94
+ ...availabilityFunctions,
95
+ ...copyFunctions,
96
+ {
97
+ local: "current",
98
+ params: [],
99
+ returns: "item()",
100
+ impl: (_, context) => {
101
+ if (context.xc.item === undefined) {
102
+ throw xsltError("XPDY0002", "current() has no current item");
103
+ }
104
+ return [context.xc.item];
105
+ },
106
+ },
107
+ {
108
+ local: "current-group",
109
+ params: [],
110
+ returns: "item()*",
111
+ impl: (_, context) => {
112
+ // absent in dynamic function calls (XSLT 3.0)
113
+ if (context.xc.group === undefined || context.dynamicCall) {
114
+ throw xsltError(
115
+ "XTDE1061",
116
+ "current-group(): there is no current group",
117
+ );
118
+ }
119
+ return context.xc.group;
120
+ },
121
+ },
122
+ {
123
+ local: "current-grouping-key",
124
+ params: [],
125
+ returns: "xs:anyAtomicType*",
126
+ impl: (_, context) => {
127
+ if (context.xc.groupKey === undefined || context.dynamicCall) {
128
+ throw xsltError("XTDE1071", "current-grouping-key(): there is no key");
129
+ }
130
+ return context.xc.groupKey;
131
+ },
132
+ },
133
+ {
134
+ local: "regex-group",
135
+ params: ["xs:integer"],
136
+ returns: "xs:string",
137
+ impl: ([[n]], context) => [
138
+ stringItem(context.xc.regex?.[Number(n.value)] ?? ""),
139
+ ],
140
+ },
141
+ {
142
+ local: "key",
143
+ params: ["xs:string", "xs:anyAtomicType*"],
144
+ returns: "node()*",
145
+ focus: true,
146
+ impl: key,
147
+ },
148
+ {
149
+ local: "key",
150
+ params: ["xs:string", "xs:anyAtomicType*", "node()"],
151
+ returns: "node()*",
152
+ impl: key,
153
+ },
154
+ {
155
+ local: "document",
156
+ params: ["item()*"],
157
+ returns: "node()*",
158
+ impl: documentFunction,
159
+ },
160
+ {
161
+ local: "document",
162
+ params: ["item()*", "node()"],
163
+ returns: "node()*",
164
+ impl: documentFunction,
165
+ },
166
+ {
167
+ local: "current-output-uri",
168
+ params: [],
169
+ returns: "xs:anyURI?",
170
+ impl: (_, context) => {
171
+ // absent in temporary output state, patterns and dynamic calls
172
+ const { temporary, outputUri } = context.xc;
173
+ return temporary || context.dynamicCall || outputUri === undefined
174
+ ? []
175
+ : [new AtomicValue(types.anyURI, outputUri)];
176
+ },
177
+ },
178
+ // the DOM gives no unparsed entities: their URI and public id are ""
179
+ ...["unparsed-entity-uri", "unparsed-entity-public-id"].flatMap((local) =>
180
+ [["xs:string"], ["xs:string", "node()"]].map((params) => ({
181
+ local,
182
+ params,
183
+ returns: "xs:string",
184
+ impl: () => [stringItem("")],
185
+ })),
186
+ ),
187
+ ...accumulatorFunctions,
188
+ ...mergeFunctions,
189
+ ...streamAvailableFunctions,
190
+ ...transformFunctions,
191
+ ];
@@ -0,0 +1,108 @@
1
+ /**
2
+ * The groups of xsl:for-each-group (XSLT 3.0 section 14): by grouping
3
+ * keys (group-by, group-adjacent, single or composite keys) and by
4
+ * patterns (group-starting-with, group-ending-with).
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/groups
7
+ */
8
+
9
+ import { atomize } from "../../xdm/nodes.js";
10
+ import { xsltError } from "../names.js";
11
+ import { patternMatches } from "../patterns/compile.js";
12
+ import { comparisonKey } from "./atomicKeys.js";
13
+ import { withFocus } from "./context.js";
14
+
15
+ /**
16
+ * A group in the making.
17
+ * @typedef {object} Group
18
+ * @property {Array} items - Its population, in order
19
+ * @property {Array|undefined} key - Its grouping key
20
+ * @property {string} [keyString] - The key compared
21
+ */
22
+
23
+ /**
24
+ * The grouping keys of an item, each with the string it is compared by:
25
+ * one per value, or one for the whole sequence of a composite key.
26
+ * @param {Array} values - Atomized key values
27
+ * @param {object} collation
28
+ * @param {boolean} composite
29
+ * @returns {Array<{key: Array, keyString: string}>}
30
+ */
31
+ function keysOf(values, collation, composite) {
32
+ const strings = values.map((value) => comparisonKey(value, collation));
33
+ if (composite) return [{ key: values, keyString: JSON.stringify(strings) }];
34
+ return values.map((value, i) => ({ key: [value], keyString: strings[i] }));
35
+ }
36
+
37
+ /**
38
+ * Groups by keys (group-by, group-adjacent).
39
+ * @param {Array} items
40
+ * @param {(xc: object) => Array} evaluateKey - Evaluates the key expression
41
+ * @param {object} xc
42
+ * @param {{adjacent: boolean, collation: object, composite: boolean}} how
43
+ * @returns {Group[]}
44
+ * @throws {import("../../errors.js").XPathError} XTTE1100 for an
45
+ * adjacent key that is not a single value
46
+ */
47
+ export function groupByKeys(items, evaluateKey, xc, how) {
48
+ const { adjacent, collation, composite } = how;
49
+ const groups = [];
50
+ const byKey = new Map();
51
+ items.forEach((item, i) => {
52
+ const values = atomize(
53
+ evaluateKey(withFocus(xc, item, i + 1, items.length)),
54
+ );
55
+ if (adjacent && !composite && values.length !== 1) {
56
+ throw xsltError("XTTE1100", "A grouping key must be a single value");
57
+ }
58
+ const keys = keysOf(values, collation, composite);
59
+ if (adjacent) {
60
+ const [{ key, keyString }] = keys;
61
+ const last = groups.at(-1);
62
+ if (last && last.keyString === keyString) last.items.push(item);
63
+ else groups.push({ items: [item], key, keyString });
64
+ return;
65
+ }
66
+ const seen = new Set();
67
+ for (const { key, keyString } of keys) {
68
+ if (seen.has(keyString)) continue;
69
+ seen.add(keyString);
70
+ let group = byKey.get(keyString);
71
+ if (!group) {
72
+ group = { items: [], key };
73
+ byKey.set(keyString, group);
74
+ groups.push(group);
75
+ }
76
+ group.items.push(item);
77
+ }
78
+ });
79
+ return groups;
80
+ }
81
+
82
+ /**
83
+ * Groups by patterns (group-starting-with, group-ending-with); in XSLT 3.0
84
+ * the population may hold any items.
85
+ * @param {Array} items
86
+ * @param {object} pattern - Compiled pattern
87
+ * @param {object} xc
88
+ * @param {boolean} starting - group-starting-with
89
+ * @returns {Group[]}
90
+ */
91
+ export function groupByPattern(items, pattern, xc, starting) {
92
+ const groups = [];
93
+ let current = null;
94
+ items.forEach((item, i) => {
95
+ const matches = patternMatches(
96
+ pattern,
97
+ item,
98
+ withFocus(xc, item, i + 1, items.length),
99
+ );
100
+ if (!current || (starting && matches && current.items.length > 0)) {
101
+ current = { items: [], key: undefined };
102
+ groups.push(current);
103
+ }
104
+ current.items.push(item);
105
+ if (!starting && matches) current = null;
106
+ });
107
+ return groups;
108
+ }
@@ -0,0 +1,156 @@
1
+ /**
2
+ * The invocation of a stylesheet (XSLT 3.0 section 2.3): an initial
3
+ * named template, an initial mode applied to the initial match selection,
4
+ * or an initial function; then the principal result as a document (a
5
+ * sequence when build-tree is "no", see rawResults.js).
6
+ *
7
+ * @module @tradik/xslt3/xslt/runtime/invocation
8
+ */
9
+
10
+ import { toSequence } from "../../xpath/eval/values.js";
11
+ import { isEntryPoint } from "../compiler/packageChecks.js";
12
+ import { xsltError } from "../names.js";
13
+ import { applyTemplates, invokeTemplate, NO_ARGS } from "./apply.js";
14
+ import { callFunction } from "./functionCall.js";
15
+ import { clarkName, normalizeParams } from "./params.js";
16
+ import { resultReceiver } from "./rawResults.js";
17
+
18
+ /** The default initial template of XSLT 3.0. */
19
+ const INITIAL_TEMPLATE =
20
+ "{http://www.w3.org/1999/XSL/Transform}initial-template";
21
+
22
+ /**
23
+ * A result tree as a Document when it is well-formed (one element, no
24
+ * text at the top), else the document fragment that holds it.
25
+ * @param {DocumentFragment} fragment
26
+ * @param {() => Document} createDocument
27
+ * @returns {Document|DocumentFragment}
28
+ */
29
+ export function finishTree(fragment, createDocument) {
30
+ const children = [...fragment.childNodes];
31
+ const elements = children.filter((child) => child.nodeType === 1).length;
32
+ if (elements !== 1 || children.some((child) => child.nodeType === 3)) {
33
+ return fragment;
34
+ }
35
+ const document = createDocument();
36
+ for (const child of children) {
37
+ document.appendChild(document.importNode(child, true));
38
+ }
39
+ return document;
40
+ }
41
+
42
+ /**
43
+ * The principal result tree: the document that owns the fragment when
44
+ * the tree is well-formed (one element, no text at the top), else the
45
+ * fragment.
46
+ * @param {DocumentFragment} fragment
47
+ * @param {Document} document - Its owner, still empty
48
+ * @returns {Document|DocumentFragment}
49
+ */
50
+ function asDocument(fragment, document) {
51
+ const children = [...fragment.childNodes];
52
+ const wellFormed =
53
+ children.filter((child) => child.nodeType === 1).length === 1 &&
54
+ !children.some((child) => child.nodeType === 3);
55
+ if (!wellFormed) return fragment;
56
+ document.appendChild(fragment);
57
+ return document;
58
+ }
59
+
60
+ /**
61
+ * The first step of a transformation: the initial template or mode.
62
+ * @param {object} tx - The transformation
63
+ * @param {object} options - See CompiledStylesheet.transform
64
+ * @param {Node|undefined} source
65
+ * @returns {Function} a step (see machine.js)
66
+ */
67
+ function initialStep(tx, options, source) {
68
+ const { stylesheet } = tx;
69
+ const args = {
70
+ params: normalizeParams(options.templateParams),
71
+ tunnel: normalizeParams(options.tunnelParams),
72
+ };
73
+ let name = options.initialTemplate;
74
+ const fromItems =
75
+ options.initialMode !== undefined ||
76
+ source !== undefined ||
77
+ options.initialMatchSelection !== undefined;
78
+ if (name === undefined && !fromItems) name = INITIAL_TEMPLATE;
79
+ if (name !== undefined) {
80
+ const key = clarkName(name);
81
+ const template = stylesheet.namedTemplates.get(key);
82
+ if (!template || !isEntryPoint(stylesheet, "template", key)) {
83
+ throw xsltError("XTDE0040", `No public template named ${name}`);
84
+ }
85
+ const supplied = args.params.size || args.tunnel.size ? args : NO_ARGS;
86
+ return (xc, out, machine) =>
87
+ invokeTemplate(template, xc, out, machine, supplied);
88
+ }
89
+ const selection =
90
+ options.initialMatchSelection ?? (source === undefined ? [] : [source]);
91
+ if (source === undefined && options.initialMatchSelection === undefined) {
92
+ throw xsltError("XTDE0044", "No source document and no initial template");
93
+ }
94
+ return (xc, out, machine) =>
95
+ applyTemplates(
96
+ toSequence(selection),
97
+ tx.defaultMode,
98
+ xc,
99
+ out,
100
+ machine,
101
+ args,
102
+ );
103
+ }
104
+
105
+ /**
106
+ * Calls the initial function.
107
+ * @param {object} tx
108
+ * @param {{name: string, args: Array}} entry
109
+ * @returns {Array} its result
110
+ */
111
+ function callInitialFunction(tx, { name, args }) {
112
+ const key = `${clarkName(name)}#${args.length}`;
113
+ const compiled = tx.stylesheet.functionBodies.get(key);
114
+ if (!compiled || !isEntryPoint(tx.stylesheet, "function", key)) {
115
+ throw xsltError("XTDE0041", `No public function ${key}`);
116
+ }
117
+ return callFunction(compiled, args.map(toSequence), { xc: tx.globalContext });
118
+ }
119
+
120
+ /**
121
+ * Runs the initial template, mode or function.
122
+ * @param {object} tx
123
+ * @param {object} options
124
+ * @param {Node|undefined} source
125
+ * @param {() => Document} createDocument
126
+ * @returns {{principal: *, principalOutput: object|null, secondary: Map,
127
+ * messages: Array}} principalOutput: the parameters of an
128
+ * xsl:result-document that wrote the principal result
129
+ */
130
+ export function invoke(tx, options, source, createDocument) {
131
+ const result = { secondary: tx.secondary, messages: tx.messages };
132
+ if (options.initialFunction) {
133
+ return {
134
+ principal: callInitialFunction(tx, options.initialFunction),
135
+ ...result,
136
+ };
137
+ }
138
+ const step = initialStep(tx, options, source);
139
+ const document = createDocument();
140
+ const { receiver, value } = resultReceiver(
141
+ tx.stylesheet.outputFor(null),
142
+ () => document,
143
+ tx.options.buildTree,
144
+ );
145
+ tx.machine.runBody([step], tx.globalContext, receiver);
146
+ const raw = value();
147
+ const written = Array.isArray(raw) ? raw.length > 0 : raw.hasChildNodes();
148
+ if (tx.principalOverride && written) {
149
+ throw xsltError("XTDE1490", "The principal result is written twice");
150
+ }
151
+ let principal = tx.principalOverride ?? raw;
152
+ if (principal === raw && !Array.isArray(raw)) {
153
+ principal = asDocument(raw, document);
154
+ }
155
+ return { principal, principalOutput: tx.principalOutput, ...result };
156
+ }
@@ -0,0 +1,107 @@
1
+ /**
2
+ * The frame that runs xsl:iterate on the work stack (XSLT 3.0 section
3
+ * 7.2): one body per item, pushed after the previous one is done, so a
4
+ * long input sequence costs no JavaScript recursion. Each body sees the
5
+ * iteration state in `xc.iteration`, where xsl:next-iteration leaves the
6
+ * new parameter values and xsl:break marks the end.
7
+ *
8
+ * @module @tradik/xslt3/xslt/runtime/iteration
9
+ */
10
+
11
+ import { derive } from "./context.js";
12
+ import { BodyFrame } from "./machine.js";
13
+
14
+ /** Runs the iterations of an xsl:iterate, then its xsl:on-completion. */
15
+ export class IterateFrame {
16
+ /**
17
+ * @param {object} spec
18
+ * @param {Array} spec.items - The input sequence
19
+ * @param {object[]} spec.params - Compiled xsl:param children
20
+ * @param {import("./machine.js").Body} spec.steps - The body
21
+ * @param {import("./machine.js").Body} spec.completion - xsl:on-completion
22
+ * @param {object} spec.xc - Context of the instruction
23
+ * @param {object} spec.out - Receiver
24
+ */
25
+ constructor({ items, params, steps, completion, xc, out }) {
26
+ this.items = items;
27
+ this.params = params;
28
+ this.steps = steps;
29
+ this.completion = completion;
30
+ this.xc = xc;
31
+ this.out = out;
32
+ this.index = 0;
33
+ /** @type {Array[]|null} the parameter values, computed on the first advance */
34
+ this.values = null;
35
+ this.state = { next: null, broken: false };
36
+ this.done = false;
37
+ }
38
+
39
+ /**
40
+ * The initial values of the parameters, each seeing the ones before.
41
+ * @param {import("./machine.js").Machine} machine
42
+ * @returns {Array[]}
43
+ */
44
+ initialValues(machine) {
45
+ const values = [];
46
+ let ctx = this.xc;
47
+ for (const param of this.params) {
48
+ const value = param.value(ctx, machine);
49
+ values.push(value);
50
+ ctx = derive(ctx, { env: { value, next: ctx.env } });
51
+ }
52
+ return values;
53
+ }
54
+
55
+ /** Applies the values given by xsl:next-iteration. */
56
+ applyNext() {
57
+ const next = this.state.next;
58
+ if (!next) return;
59
+ this.state.next = null;
60
+ this.params.forEach((param, i) => {
61
+ if (next.has(param.key)) {
62
+ this.values[i] = param.convert(next.get(param.key));
63
+ }
64
+ });
65
+ }
66
+
67
+ /**
68
+ * The context of a body: the parameters bound, a focus or none.
69
+ * @param {object} focus - `{item, position, size}`
70
+ * @returns {object}
71
+ */
72
+ context(focus) {
73
+ let env = this.xc.env;
74
+ for (const value of this.values) env = { value, next: env };
75
+ return derive(this.xc, {
76
+ ...focus,
77
+ env,
78
+ rule: null,
79
+ iteration: this.state,
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Pushes the next body, or the completion.
85
+ * @param {import("./machine.js").Machine} machine
86
+ * @returns {boolean} false when the iteration is over
87
+ */
88
+ advance(machine) {
89
+ if (this.done || this.state.broken) return false;
90
+ if (this.values === null) this.values = this.initialValues(machine);
91
+ else this.applyNext();
92
+ const size = this.items.length;
93
+ if (this.index < size) {
94
+ const item = this.items[this.index++];
95
+ const xc = this.context({ item, position: this.index, size });
96
+ if (this.steps.length > 0) {
97
+ machine.push(new BodyFrame(this.steps, xc, this.out));
98
+ }
99
+ return true;
100
+ }
101
+ this.done = true;
102
+ if (this.completion.length === 0) return false;
103
+ const xc = this.context({ item: undefined, position: 0, size: 0 });
104
+ machine.push(new BodyFrame(this.completion, xc, this.out));
105
+ return true;
106
+ }
107
+ }