@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,190 @@
1
+ /**
2
+ * xsl:merge, xsl:merge-source, xsl:merge-key and xsl:merge-action
3
+ * (XSLT 3.0 section 15): the static checks and the compiled sources; the
4
+ * merging itself is in runtime/merging.js.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/merge
7
+ */
8
+
9
+ import { atomize, stringValue } from "../../xdm/nodes.js";
10
+ import { types } from "../../xdm/types.js";
11
+ import { resolveUri } from "../../xpath/eval/uris.js";
12
+ import { useAccumulators } from "../compiler/useAccumulators.js";
13
+ import { checkAttributes, required, yesNo } from "../compiler/attributes.js";
14
+ import { compileBody } from "../compiler/body.js";
15
+ import { setApplicable } from "../runtime/accumulators.js";
16
+ import {
17
+ derive,
18
+ dynamicContextOf,
19
+ evaluate,
20
+ withFocus,
21
+ } from "../runtime/context.js";
22
+ import { BodyFrame, LoopFrame } from "../runtime/machine.js";
23
+ import { mergeGroups } from "../runtime/merging.js";
24
+ import { attr, isNCName, isXsl, xsltError } from "../names.js";
25
+ import { compileSortKey } from "./sort.js";
26
+ import { checkNoValidation } from "./sourceDocument.js";
27
+
28
+ /** Types a URI of for-each-source may have. */
29
+ const URI_TYPES = new Set([types.string, types.anyURI, types.untypedAtomic]);
30
+
31
+ /**
32
+ * Compiles an xsl:merge-key.
33
+ * @param {Element} element
34
+ * @param {object} cx
35
+ * @param {object} scope
36
+ * @returns {object} the key, compiled as an xsl:sort
37
+ */
38
+ function compileMergeKey(element, cx, scope) {
39
+ if (!isXsl(element, "merge-key")) {
40
+ throw xsltError("XTSE0010", "xsl:merge-source contains xsl:merge-key");
41
+ }
42
+ if (attr(element, "select") !== undefined && cx.children(element).length) {
43
+ throw xsltError("XTSE3200", "xsl:merge-key has select and content");
44
+ }
45
+ return compileSortKey(element, cx, scope);
46
+ }
47
+
48
+ /**
49
+ * The URIs of for-each-source as strings.
50
+ * @param {Array} items
51
+ * @returns {string[]}
52
+ */
53
+ function uriStrings(items) {
54
+ return atomize(items).map((value) => {
55
+ if (!URI_TYPES.has(value.type)) {
56
+ throw xsltError("XPTY0004", "for-each-source must give URIs");
57
+ }
58
+ return stringValue(value);
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Compiles an xsl:merge-source.
64
+ * @param {Element} element
65
+ * @param {object} cx
66
+ * @param {object} scope
67
+ * @param {number} index - Position among the sources
68
+ * @returns {object} `{name, keys, sortBeforeMerge, inputs(xc, machine)}`
69
+ */
70
+ function compileMergeSource(element, cx, scope, index) {
71
+ checkAttributes(element);
72
+ const has = (name) => attr(element, name) !== undefined;
73
+ const forEachSource = has("for-each-source");
74
+ if (
75
+ (has("for-each-item") &&
76
+ (forEachSource || has("use-accumulators") || has("streamable"))) ||
77
+ (has("use-accumulators") && !forEachSource)
78
+ ) {
79
+ throw xsltError("XTSE3195", "Conflicting attributes on xsl:merge-source");
80
+ }
81
+ if (!forEachSource && (has("validation") || has("type"))) {
82
+ throw xsltError("XTSE0010", "validation and type need for-each-source");
83
+ }
84
+ if (has("validation") && has("type")) {
85
+ throw xsltError("XTSE1505", "validation and type are exclusive");
86
+ }
87
+ checkNoValidation(element);
88
+ yesNo(element, "streamable", false);
89
+ const name = attr(element, "name")?.trim();
90
+ if (name !== undefined && !isNCName(name)) {
91
+ throw xsltError("XTSE0020", `Invalid merge source name ${name}`);
92
+ }
93
+ const xpath = (text) => cx.exprs.xpath(text, element, scope.vars);
94
+ const select = xpath(required(element, "select"));
95
+ const itemExpr = has("for-each-item")
96
+ ? xpath(attr(element, "for-each-item"))
97
+ : null;
98
+ const sourceExpr = forEachSource
99
+ ? xpath(attr(element, "for-each-source"))
100
+ : null;
101
+ const accumulators = useAccumulators(
102
+ attr(element, "use-accumulators") ?? "",
103
+ element,
104
+ cx,
105
+ );
106
+ const base = cx.baseUriOf(element);
107
+ const keys = cx
108
+ .children(element)
109
+ .map((child) => compileMergeKey(child, cx, scope));
110
+ if (keys.length === 0) {
111
+ throw xsltError("XTSE0010", "xsl:merge-source needs xsl:merge-key");
112
+ }
113
+ const documents = (xc) =>
114
+ uriStrings(evaluate(sourceExpr, xc)).map((uri) => {
115
+ const document = dynamicContextOf(xc).loadDocument(resolveUri(uri, base));
116
+ setApplicable(xc.tx, document, accumulators);
117
+ return document;
118
+ });
119
+ return {
120
+ name: name ?? `#${index}`,
121
+ named: name !== undefined,
122
+ keys,
123
+ sortBeforeMerge: yesNo(element, "sort-before-merge", false),
124
+ inputs(xc) {
125
+ let anchors = null;
126
+ if (itemExpr) anchors = evaluate(itemExpr, xc);
127
+ else if (sourceExpr) anchors = documents(xc);
128
+ if (!anchors) return [evaluate(select, xc)];
129
+ return anchors.map((anchor, i) =>
130
+ evaluate(select, withFocus(xc, anchor, i + 1, anchors.length)),
131
+ );
132
+ },
133
+ };
134
+ }
135
+
136
+ /**
137
+ * xsl:merge.
138
+ * @param {Element} element
139
+ * @param {object} cx
140
+ * @param {object} scope
141
+ * @returns {Function}
142
+ */
143
+ export function compileMerge(element, cx, scope) {
144
+ const children = cx.children(element);
145
+ while (children.length > 0 && isXsl(children.at(-1), "fallback")) {
146
+ children.pop();
147
+ }
148
+ const action = children.at(-1);
149
+ const sourceElements = children.slice(0, -1);
150
+ if (
151
+ !action ||
152
+ !isXsl(action, "merge-action") ||
153
+ sourceElements.length === 0 ||
154
+ !sourceElements.every((child) => isXsl(child, "merge-source"))
155
+ ) {
156
+ throw xsltError(
157
+ "XTSE0010",
158
+ "xsl:merge contains xsl:merge-source elements then xsl:merge-action",
159
+ );
160
+ }
161
+ const sources = sourceElements.map((child, i) =>
162
+ compileMergeSource(child, cx, scope, i),
163
+ );
164
+ const names = sources.filter((s) => s.named).map((s) => s.name);
165
+ if (new Set(names).size !== names.length) {
166
+ throw xsltError("XTSE3190", "Two merge sources have the same name");
167
+ }
168
+ if (sources.some((source) => source.keys.length !== sources[0].keys.length)) {
169
+ throw xsltError("XTSE2200", "The merge sources have different key counts");
170
+ }
171
+ checkAttributes(action);
172
+ const body = compileBody(action, cx, scope);
173
+ return (xc, out, machine) => {
174
+ const groups = mergeGroups(sources, xc, machine);
175
+ const size = groups.length;
176
+ if (size === 0 || body.length === 0) return;
177
+ machine.push(
178
+ new LoopFrame(size, (i) => {
179
+ const group = groups[i];
180
+ const context = derive(
181
+ withFocus(xc, group.entries[0].item, i + 1, size),
182
+ {
183
+ merge: { group, names },
184
+ },
185
+ );
186
+ machine.push(new BodyFrame(body, context, out));
187
+ }),
188
+ );
189
+ };
190
+ }
@@ -0,0 +1,205 @@
1
+ /**
2
+ * xsl:number (XSLT 3.0 section 12): numbers from the value attribute or
3
+ * the position of a node, formatted with a format string of
4
+ * alphanumeric tokens and separators.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/number
7
+ */
8
+
9
+ import { isNode } from "../../xdm/atomic.js";
10
+ import { canonicalString } from "../../xdm/lexical.js";
11
+ import { toNumber } from "../../xdm/generalCompare.js";
12
+ import { atomize } from "../../xdm/nodes.js";
13
+ import { infoOf } from "../compiler/elementInfo.js";
14
+ import { evaluate } from "../runtime/context.js";
15
+ import {
16
+ countKey,
17
+ numberAny,
18
+ numberHierarchy,
19
+ numberMemo,
20
+ sameKindAndName,
21
+ } from "../runtime/numbering.js";
22
+ import { formatNumbers } from "../runtime/numberFormat.js";
23
+ import { avtEvaluator } from "../runtime/values.js";
24
+ import { attr, xsltError } from "../names.js";
25
+ import { compilePattern, patternMatches } from "../patterns/compile.js";
26
+
27
+ /**
28
+ * Converts the value attribute's result to integers.
29
+ * @param {Array} values
30
+ * @param {boolean} compatible
31
+ * @returns {bigint[]|string} integers, or a string for invalid numbers
32
+ * in backwards-compatible mode
33
+ */
34
+ function integersOf(values, compatible) {
35
+ const result = [];
36
+ const items = atomize(values);
37
+ // XSLT 1.0 behaviour: number(()) is NaN
38
+ if (compatible && items.length === 0) return "NaN";
39
+ // XSLT 1.0 behaviour: the first item only
40
+ for (const value of compatible ? items.slice(0, 1) : items) {
41
+ if (typeof value.value === "bigint" && value.value >= 0n) {
42
+ result.push(value.value);
43
+ continue;
44
+ }
45
+ const n = toNumber(value).value;
46
+ if (!Number.isFinite(n) || n < -0.5) {
47
+ if (compatible) return canonicalString(toNumber(value));
48
+ throw xsltError("XTDE0980", `Cannot number ${canonicalString(value)}`);
49
+ }
50
+ result.push(BigInt(Math.round(n)));
51
+ }
52
+ return result;
53
+ }
54
+
55
+ /**
56
+ * Checks a lang value: a language tag (xs:language).
57
+ * @param {string} text
58
+ * @param {string} code - XTSE0020 when static, XTDE0030 when evaluated
59
+ */
60
+ function checkLanguage(text, code) {
61
+ if (!/^\s*[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\s*$/.test(text)) {
62
+ throw xsltError(code, `Invalid lang "${text}"`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Re-bases the numbers by the start-at attribute (XSLT 3.0 section 12.1).
68
+ * @param {bigint[]} numbers
69
+ * @param {string} text - Effective value of start-at
70
+ * @returns {bigint[]}
71
+ * @throws {import("../../errors.js").XPathError} XTDE0030 for a value
72
+ * that is not a list of integers
73
+ */
74
+ function rebase(numbers, text) {
75
+ if (!/^\s*-?\d+(\s+-?\d+)*\s*$/.test(text)) {
76
+ throw xsltError("XTDE0030", `Invalid start-at "${text}"`);
77
+ }
78
+ const starts = text.trim().split(/\s+/).map(BigInt);
79
+ return numbers.map(
80
+ (number, i) => number + starts[Math.min(i, starts.length - 1)] - 1n,
81
+ );
82
+ }
83
+
84
+ /**
85
+ * Compiles an optional pattern attribute.
86
+ * @returns {object|null}
87
+ */
88
+ function optionalPattern(element, name, cx, scope) {
89
+ const text = attr(element, name);
90
+ return text === undefined
91
+ ? null
92
+ : compilePattern(text, element, cx, scope.vars);
93
+ }
94
+
95
+ /**
96
+ * Whether the numbers an instruction gives may be memoized: its count
97
+ * and from patterns must not read variables (local ones differ between
98
+ * invocations) or the dynamic context of the instruction.
99
+ * @param {Element} element
100
+ * @returns {boolean}
101
+ */
102
+ function isMemoizable(element) {
103
+ const text = `${attr(element, "count") ?? ""} ${attr(element, "from") ?? ""}`;
104
+ return !/\$|current-|regex-group/.test(text);
105
+ }
106
+
107
+ /**
108
+ * xsl:number.
109
+ * @param {Element} element
110
+ * @param {object} cx
111
+ * @param {object} scope
112
+ * @returns {Function}
113
+ */
114
+ export function compileNumber(element, cx, scope) {
115
+ const expr = (name) => {
116
+ const text = attr(element, name);
117
+ return text === undefined
118
+ ? null
119
+ : cx.exprs.xpath(text, element, scope.vars);
120
+ };
121
+ const avt = (name, fallback) => {
122
+ const text = attr(element, name);
123
+ return text === undefined
124
+ ? () => fallback
125
+ : avtEvaluator(cx.exprs.avt(text, element, scope.vars));
126
+ };
127
+ const value = expr("value");
128
+ const select = expr("select");
129
+ const level = attr(element, "level")?.trim() ?? "single";
130
+ if (!["single", "multiple", "any"].includes(level)) {
131
+ throw xsltError("XTSE0020", `Invalid level ${level}`);
132
+ }
133
+ const count = optionalPattern(element, "count", cx, scope);
134
+ const from = optionalPattern(element, "from", cx, scope);
135
+ if (value && (select || count || from || attr(element, "level"))) {
136
+ throw xsltError(
137
+ "XTSE0975",
138
+ "xsl:number value excludes select, level, count, from",
139
+ );
140
+ }
141
+ const format = avt("format", "1");
142
+ const ordinal = avt("ordinal", "");
143
+ const separator = avt("grouping-separator", "");
144
+ const size = avt("grouping-size", "");
145
+ const compatible = infoOf(element).version < 2;
146
+ const startAt =
147
+ attr(element, "start-at") === undefined ? null : avt("start-at");
148
+ const langText = attr(element, "lang");
149
+ if (langText !== undefined && !langText.includes("{")) {
150
+ checkLanguage(langText, "XTSE0020");
151
+ }
152
+ const lang = langText === undefined ? null : avt("lang");
153
+ // identity of the instruction for the memos of each transformation
154
+ const instruction = {};
155
+ const memoizable = isMemoizable(element);
156
+ return (xc, out) => {
157
+ let numbers;
158
+ if (value) {
159
+ numbers = integersOf(evaluate(value, xc), compatible);
160
+ if (typeof numbers === "string") {
161
+ out.text(numbers);
162
+ return;
163
+ }
164
+ } else {
165
+ const nodes = select ? evaluate(select, xc) : [xc.item];
166
+ if (nodes.length !== 1 || !isNode(nodes[0])) {
167
+ throw xsltError(
168
+ select ? "XTTE1000" : "XTTE0990",
169
+ "xsl:number needs one node to number",
170
+ );
171
+ }
172
+ const node = nodes[0];
173
+ const countTest = count
174
+ ? (n) => patternMatches(count, n, xc)
175
+ : sameKindAndName(node);
176
+ const fromTest = from ? (n) => patternMatches(from, n, xc) : null;
177
+ const memo = memoizable
178
+ ? numberMemo(xc.tx, instruction, count ? "" : countKey(node))
179
+ : null;
180
+ const places =
181
+ level === "any"
182
+ ? numberAny(node, countTest, fromTest, memo)
183
+ : numberHierarchy(
184
+ node,
185
+ countTest,
186
+ fromTest,
187
+ level === "multiple",
188
+ memo,
189
+ );
190
+ numbers = places.map(BigInt);
191
+ }
192
+ if (startAt) numbers = rebase(numbers, startAt(xc));
193
+ const language = lang ? lang(xc) : null;
194
+ if (language !== null) checkLanguage(language, "XTDE0030");
195
+ const ordinalText = ordinal(xc).trim();
196
+ out.text(
197
+ formatNumbers(numbers, format(xc), {
198
+ ordinal: ordinalText !== "" && ordinalText !== "no" && ordinalText,
199
+ lang: language,
200
+ separator: separator(xc),
201
+ size: Number(size(xc)),
202
+ }),
203
+ );
204
+ };
205
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * xsl:message (XSLT 3.0 section 22.1): messages collected as documents,
3
+ * optionally terminating the transformation.
4
+ *
5
+ * @module @tradik/xslt3/xslt/instructions/output
6
+ */
7
+
8
+ import { stringItem } from "../../xpath/eval/atomics.js";
9
+ import { compileBody } from "../compiler/body.js";
10
+ import { evaluate } from "../runtime/context.js";
11
+ import { TreeReceiver } from "../runtime/treeReceiver.js";
12
+ import { avtEvaluator, bodySequence } from "../runtime/values.js";
13
+ import { attr, isNCName, isXsl, xsltError } from "../names.js";
14
+
15
+ /** Values of yes/no attributes: the "yes" ones first. */
16
+ const YES_NO = ["yes", "true", "1", "no", "false", "0"];
17
+
18
+ /**
19
+ * Builds a document node holding items.
20
+ * @param {Array} items
21
+ * @param {object} xc
22
+ * @returns {Node} a document fragment
23
+ */
24
+ function buildDocument(items, xc) {
25
+ const fragment = xc.tx.scratch().createDocumentFragment();
26
+ const out = new TreeReceiver(fragment, new Map());
27
+ for (const item of items) out.item(item);
28
+ return fragment;
29
+ }
30
+
31
+ /**
32
+ * xsl:message.
33
+ * @param {Element} element
34
+ * @param {object} cx
35
+ * @param {object} scope
36
+ * @returns {Function}
37
+ */
38
+ export function compileMessage(element, cx, scope) {
39
+ const selectText = attr(element, "select");
40
+ const select =
41
+ selectText === undefined
42
+ ? null
43
+ : cx.exprs.xpath(selectText, element, scope.vars);
44
+ const body = compileBody(
45
+ element,
46
+ cx,
47
+ scope,
48
+ cx.children(element).filter((child) => !isXsl(child, "fallback")),
49
+ );
50
+ const terminateText = attr(element, "terminate");
51
+ if (
52
+ terminateText !== undefined &&
53
+ !terminateText.includes("{") &&
54
+ !YES_NO.includes(terminateText.trim())
55
+ ) {
56
+ throw xsltError("XTSE0020", `Invalid terminate value ${terminateText}`);
57
+ }
58
+ const terminate = terminateText
59
+ ? avtEvaluator(cx.exprs.avt(terminateText, element, scope.vars))
60
+ : () => "no";
61
+ const codeText = attr(element, "error-code");
62
+ const errorCode = codeText
63
+ ? avtEvaluator(cx.exprs.avt(codeText, element, scope.vars))
64
+ : () => "XTMM9000";
65
+ return (xc, out, machine) => {
66
+ let items;
67
+ try {
68
+ items = [
69
+ ...(select ? evaluate(select, xc) : []),
70
+ ...bodySequence(body, xc, machine, false),
71
+ ];
72
+ } catch (error) {
73
+ if (typeof error?.code !== "string") throw error;
74
+ // XSLT 3.0 section 23.1: an error in the content of a message is
75
+ // reported in the message, not by failing the transformation
76
+ items = [stringItem(`Error in xsl:message: ${error.message}`)];
77
+ }
78
+ let message;
79
+ try {
80
+ message = buildDocument(items, xc);
81
+ } catch {
82
+ // XSLT 3.0: a message that cannot be a document does not fail
83
+ message = items;
84
+ }
85
+ xc.tx.messages.push(message);
86
+ xc.tx.onMessage?.(message);
87
+ const value = terminate(xc).trim();
88
+ if (!YES_NO.includes(value)) {
89
+ throw xsltError("XTDE0030", `Invalid terminate value ${value}`);
90
+ }
91
+ if (YES_NO.indexOf(value) < 3) {
92
+ const code = errorCode(xc).trim();
93
+ const local = code.replace(/^Q\{[^}]*\}/, "").replace(/^[^:]*:/, "");
94
+ const error = xsltError(
95
+ isNCName(local) ? local : "XTMM9000",
96
+ "Terminated by xsl:message",
97
+ );
98
+ error.value = message;
99
+ throw error;
100
+ }
101
+ };
102
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Parameters (XSLT 3.0 sections 9.2 and 10.1.1): the xsl:param
3
+ * declarations of templates and functions, and the xsl:with-param
4
+ * children of invocations.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/params
7
+ */
8
+
9
+ import { checkAttributes, yesNo } from "../compiler/attributes.js";
10
+ import { displayName, isXsl, xsltError } from "../names.js";
11
+ import { compileValue, typeConverter, variableName } from "./variables.js";
12
+
13
+ /**
14
+ * Compiles the xsl:with-param children of an instruction.
15
+ * @param {Node[]} children
16
+ * @param {object} cx
17
+ * @param {object} scope
18
+ * @returns {{names: Set<string>,
19
+ * evaluate: (xc: object, machine: object) => import("../runtime/apply.js").Args}}
20
+ */
21
+ export function compileWithParams(children, cx, scope) {
22
+ const compiled = [];
23
+ for (const child of children) {
24
+ if (!isXsl(child, "with-param")) continue;
25
+ checkAttributes(child);
26
+ const key = variableName(child, cx);
27
+ if (compiled.some((param) => param.key === key)) {
28
+ throw xsltError("XTSE0670", `Duplicate parameter ${key}`);
29
+ }
30
+ const { value } = compileValue(child, cx, scope, "XTTE0570");
31
+ compiled.push({ key, value, tunnel: yesNo(child, "tunnel", false) });
32
+ }
33
+ return {
34
+ names: new Set(compiled.filter((p) => !p.tunnel).map((p) => p.key)),
35
+ evaluate(xc, machine) {
36
+ const params = new Map();
37
+ const tunnel = new Map();
38
+ for (const param of compiled) {
39
+ const target = param.tunnel ? tunnel : params;
40
+ target.set(param.key, param.value(xc, machine));
41
+ }
42
+ return { params, tunnel };
43
+ },
44
+ };
45
+ }
46
+
47
+ /**
48
+ * The default value of a parameter with a type and no default: the
49
+ * empty sequence, or XTDE0610 when the type does not allow it.
50
+ * @param {object} compiled - See variables.compileValue
51
+ * @param {string} key
52
+ * @returns {(xc: object, machine: object) => Array}
53
+ */
54
+ function implicitDefault(compiled, key) {
55
+ return (xc, machine) => {
56
+ try {
57
+ return compiled.value(xc, machine);
58
+ } catch (error) {
59
+ throw error.code === "XTTE0590"
60
+ ? xsltError("XTDE0610", `No value for the parameter ${key}`)
61
+ : error;
62
+ }
63
+ };
64
+ }
65
+
66
+ /**
67
+ * The parameters of a template or function, compiled in order (each
68
+ * default value sees the parameters before it).
69
+ * @param {Element[]} elements - The xsl:param children
70
+ * @param {object} cx
71
+ * @param {object} scope
72
+ * @param {boolean} [isFunction]
73
+ * @returns {{params: object[], scope: object}}
74
+ */
75
+ export function compileParams(elements, cx, scope, isFunction = false) {
76
+ const params = [];
77
+ let current = scope;
78
+ for (const element of elements) {
79
+ checkAttributes(element);
80
+ const key = variableName(element, cx);
81
+ if (params.some((param) => param.key === key)) {
82
+ throw xsltError("XTSE0580", `Duplicate parameter ${key}`);
83
+ }
84
+ const compiled = compileValue(element, cx, current, "XTTE0590");
85
+ if (isFunction && compiled.hasDefault) {
86
+ throw xsltError("XTSE0760", "A function parameter cannot have a default");
87
+ }
88
+ const required = isFunction || yesNo(element, "required", false);
89
+ if (!isFunction && required && compiled.hasDefault) {
90
+ throw xsltError("XTSE0010", "A required parameter cannot have a default");
91
+ }
92
+ params.push({
93
+ key,
94
+ required,
95
+ tunnel: yesNo(element, "tunnel", false),
96
+ value:
97
+ compiled.hasDefault || !compiled.type
98
+ ? compiled.value
99
+ : implicitDefault(compiled, key),
100
+ type: compiled.type,
101
+ convert: typeConverter(
102
+ compiled.type,
103
+ "XTTE0590",
104
+ `parameter $${displayName(key)}`,
105
+ ),
106
+ });
107
+ current = { ...current, vars: { key, next: current.vars } };
108
+ }
109
+ return { params, scope: current };
110
+ }