@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
+ * Keys (XSLT 3.0 section 20.2): for each xsl:key name and document, an
3
+ * index from key values to the nodes that match the key's pattern, built
4
+ * on the first key() call for that document.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/keys
7
+ */
8
+
9
+ import { getCollation } from "../../functions/collations.js";
10
+ import { stringItem } from "../../xpath/eval/atomics.js";
11
+ import { attributesOf } from "../../xpath/eval/domNodes.js";
12
+ import { namespaceNodesOf } from "../../xpath/eval/namespaceNodes.js";
13
+ import { atomize } from "../../xdm/nodes.js";
14
+ import { canonicalString } from "../../xdm/lexical.js";
15
+ import { xsltError } from "../names.js";
16
+ import { patternMatches } from "../patterns/compile.js";
17
+ import { comparisonKey, NAN_KEY } from "./atomicKeys.js";
18
+ import { derive, evaluate } from "./context.js";
19
+ import { bodySequence } from "./values.js";
20
+
21
+ /**
22
+ * The index string of a key value.
23
+ * @param {*} value - Atomic value
24
+ * @param {object} definition - Key definition (collation, compatible)
25
+ * @returns {string}
26
+ */
27
+ export function keyValueString(value, definition) {
28
+ const atomic = definition.compatible
29
+ ? stringItem(canonicalString(value))
30
+ : value;
31
+ const collation = definition.collation
32
+ ? getCollation(definition.collation)
33
+ : null;
34
+ return comparisonKey(atomic, collation);
35
+ }
36
+
37
+ /**
38
+ * The index strings of the key values of a node, or of the values sought:
39
+ * one per value, or one for the whole sequence of a composite key (XSLT
40
+ * 3.0 section 20.2.2), which matches only an equal sequence.
41
+ * @param {Array} values - Atomic values
42
+ * @param {object} definition - Key definition
43
+ * @returns {string[]}
44
+ */
45
+ function indexKeys(values, definition) {
46
+ const keys = values.map((value) => keyValueString(value, definition));
47
+ if (!definition.composite) return keys;
48
+ return keys.includes(NAN_KEY) ? [NAN_KEY] : [JSON.stringify(keys)];
49
+ }
50
+
51
+ /**
52
+ * All nodes of a tree in document order (elements, attributes, text...).
53
+ * @param {Node} root
54
+ * @param {boolean} namespaces - Include the namespace nodes
55
+ * @returns {Node[]}
56
+ */
57
+ function treeNodes(root, namespaces) {
58
+ const nodes = [];
59
+ const stack = [root];
60
+ while (stack.length > 0) {
61
+ const node = stack.pop();
62
+ nodes.push(node);
63
+ if (node.nodeType === 1) {
64
+ if (namespaces) nodes.push(...namespaceNodesOf(node));
65
+ nodes.push(...attributesOf(node));
66
+ }
67
+ const children = node.childNodes ?? [];
68
+ for (let i = children.length - 1; i >= 0; i--) {
69
+ if (children[i].nodeType !== 10) stack.push(children[i]);
70
+ }
71
+ }
72
+ return nodes;
73
+ }
74
+
75
+ /**
76
+ * Builds the index of a key for a tree.
77
+ * @param {object[]} definitions - The xsl:key declarations of the name
78
+ * @param {Node} root
79
+ * @param {object} xc
80
+ * @returns {Map<string, Node[]>}
81
+ */
82
+ function buildIndex(definitions, root, xc) {
83
+ const index = new Map();
84
+ const machine = xc.tx.machine;
85
+ const namespaces = definitions.some((definition) => definition.namespaces);
86
+ for (const node of treeNodes(root, namespaces)) {
87
+ for (const definition of definitions) {
88
+ const nodeXc = derive(xc, { item: node, position: 1, size: 1 });
89
+ if (!patternMatches(definition.match, node, nodeXc)) continue;
90
+ const values = definition.use
91
+ ? evaluate(definition.use, nodeXc)
92
+ : bodySequence(definition.body, nodeXc, machine);
93
+ for (const key of indexKeys(atomize(values), definition)) {
94
+ // NaN is not equal to itself: it finds nothing
95
+ if (key === NAN_KEY) continue;
96
+ let list = index.get(key);
97
+ if (!list) index.set(key, (list = []));
98
+ if (list.at(-1) !== node) list.push(node);
99
+ }
100
+ }
101
+ }
102
+ return index;
103
+ }
104
+
105
+ /**
106
+ * Looks up nodes by key.
107
+ * @param {object} xc - Context of the call
108
+ * @param {string} name - Clark name of the key
109
+ * @param {Array} values - Atomic values sought
110
+ * @param {Node} root - Root of the tree searched
111
+ * @param {object} [owner] - Package compiler of the call (default: the
112
+ * top-level package)
113
+ * @returns {Node[]} nodes in document order
114
+ */
115
+ export function keyLookup(xc, name, values, root, owner) {
116
+ const { tx } = xc;
117
+ // keys are local to the package of the call (XSLT 3.0 section 3.5.5)
118
+ const definitions = (owner ?? tx.stylesheet).keys.get(name);
119
+ if (!definitions) throw xsltError("XTDE1260", `No key named ${name}`);
120
+ let byRoot = tx.keyIndexes.get(definitions);
121
+ if (!byRoot) tx.keyIndexes.set(definitions, (byRoot = new Map()));
122
+ let index = byRoot.get(root);
123
+ if (index === "building") {
124
+ throw xsltError("XTDE0640", `The key ${name} depends on itself`);
125
+ }
126
+ if (!index) {
127
+ byRoot.set(root, "building");
128
+ const base = derive(xc, { env: tx.globalEnv, rule: null, temporary: true });
129
+ try {
130
+ index = buildIndex(definitions, root, base);
131
+ } finally {
132
+ byRoot.delete(root);
133
+ }
134
+ byRoot.set(root, index);
135
+ }
136
+ const found = new Set();
137
+ for (const key of indexKeys(values, definitions[0])) {
138
+ for (const node of index.get(key) ?? []) found.add(node);
139
+ }
140
+ return tx.dyn.order.sort([...found]);
141
+ }
@@ -0,0 +1,97 @@
1
+ /**
2
+ * Variables evaluated on first use: an entry of the variable environment
3
+ * (see xpath/eval/scope.js) whose `value` is computed when it is first
4
+ * read, so that a variable that is never used never fails, and a
5
+ * circular definition is reported (XTDE0640).
6
+ *
7
+ * An xsl:try does not catch the errors of variables declared outside it
8
+ * (XSLT 3.0 section 8.3), although they are computed when first read
9
+ * inside: each xsl:try marks the pending entries of its environment, and
10
+ * the error of a marked entry names the xsl:try instances it escapes.
11
+ *
12
+ * @module @tradik/xslt3/xslt/runtime/lazy
13
+ */
14
+
15
+ import { displayName, xsltError } from "../names.js";
16
+
17
+ const PENDING = 0;
18
+ const RUNNING = 1;
19
+ const DONE = 2;
20
+
21
+ /** Marker of the global variables: outside every xsl:try. */
22
+ export const EVERY_TRY = Symbol("every xsl:try");
23
+
24
+ /** The marks of every global variable. */
25
+ const GLOBAL = Object.freeze(new Set([EVERY_TRY]));
26
+
27
+ /**
28
+ * Marks the variables of an environment that are not computed yet as
29
+ * declared outside an xsl:try about to run.
30
+ * @param {object|null} env - The environment of the xsl:try
31
+ * @param {object|null} globalEnv - Where the global variables start
32
+ * @param {object} marker - Identifies this run of the xsl:try
33
+ */
34
+ export function markOutside(env, globalEnv, marker) {
35
+ for (let entry = env; entry && entry !== globalEnv; entry = entry.next) {
36
+ if (entry instanceof LazyEntry && entry.state !== DONE) {
37
+ entry.outside ??= new Set();
38
+ entry.outside.add(marker);
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Whether an error escapes a run of xsl:try (raised by a variable
45
+ * declared outside it).
46
+ * @param {*} error
47
+ * @param {object} marker - The run of the xsl:try
48
+ * @returns {boolean}
49
+ */
50
+ export const escapesTry = (error, marker) =>
51
+ Boolean(error?.outsideTry?.has(EVERY_TRY) || error?.outsideTry?.has(marker));
52
+
53
+ /** An environment entry computed on first use. */
54
+ export class LazyEntry {
55
+ /**
56
+ * @param {object|null} next - The enclosing entries
57
+ * @param {() => Array} compute - Computes the value
58
+ * @param {string} name - Variable name, for the circularity error
59
+ * @param {boolean} [global] - A global variable (outside every xsl:try)
60
+ */
61
+ constructor(next, compute, name, global = false) {
62
+ this.next = next;
63
+ this.compute = compute;
64
+ this.name = name;
65
+ /** @type {Set<object|symbol>|null} the xsl:try runs it is outside of */
66
+ this.outside = global ? GLOBAL : null;
67
+ this.state = PENDING;
68
+ this.cached = undefined;
69
+ }
70
+
71
+ /** @returns {Array} the value, computed once */
72
+ get value() {
73
+ if (this.state === DONE) return this.cached;
74
+ if (this.state === RUNNING) {
75
+ throw xsltError(
76
+ "XTDE0640",
77
+ `Circular definition of $${displayName(this.name)}`,
78
+ );
79
+ }
80
+ this.state = RUNNING;
81
+ try {
82
+ this.cached = this.compute();
83
+ } catch (error) {
84
+ this.state = PENDING;
85
+ if (this.outside && error instanceof Error) {
86
+ error.outsideTry = new Set([
87
+ ...(error.outsideTry ?? []),
88
+ ...this.outside,
89
+ ]);
90
+ }
91
+ throw error;
92
+ }
93
+ this.state = DONE;
94
+ this.compute = null;
95
+ return this.cached;
96
+ }
97
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Where an error of a stylesheet comes from: the module URI and, when the
3
+ * DOM records them (@xmldom/xmldom does, jsdom does not), the line and
4
+ * column of the stylesheet element. Compiled steps are mapped to their
5
+ * element at compile time; the location is worked out only when an error
6
+ * is raised, so running instructions costs nothing more.
7
+ *
8
+ * @module @tradik/xslt3/xslt/runtime/locations
9
+ */
10
+
11
+ import { XPathError } from "../../errors.js";
12
+ import { moduleUriOf } from "../compiler/elementInfo.js";
13
+
14
+ /**
15
+ * Location of an error.
16
+ * @typedef {object} ErrorLocation
17
+ * @property {string} [module] - URI of the stylesheet module
18
+ * @property {number} [line] - 1-based line of the element
19
+ * @property {number} [column] - 1-based column of the element
20
+ */
21
+
22
+ /** @type {WeakMap<object, Node>} compiled steps to stylesheet nodes */
23
+ const stepNodes = new WeakMap();
24
+
25
+ /**
26
+ * Records the stylesheet node a compiled step comes from.
27
+ * @param {Function|object|null} step
28
+ * @param {Node} node
29
+ */
30
+ export function recordStep(step, node) {
31
+ if (step) stepNodes.set(step, node);
32
+ }
33
+
34
+ /**
35
+ * The location of a stylesheet node.
36
+ * @param {Node} node - Element, or a text node (its parent is used)
37
+ * @returns {ErrorLocation}
38
+ */
39
+ export function locationOf(node) {
40
+ const element = node.nodeType === 1 ? node : node.parentNode;
41
+ const location = {};
42
+ const module = element ? moduleUriOf(element) : undefined;
43
+ if (module !== undefined) location.module = module;
44
+ if (Number.isInteger(node.lineNumber)) location.line = node.lineNumber;
45
+ if (Number.isInteger(node.columnNumber)) location.column = node.columnNumber;
46
+ return location;
47
+ }
48
+
49
+ /**
50
+ * Adds the location of a stylesheet node to an error that has none: the
51
+ * `location` property, and "(line N of URI)" in the message when the
52
+ * line is known.
53
+ * @param {*} error
54
+ * @param {Node|undefined} node
55
+ * @returns {*} the error
56
+ */
57
+ export function locate(error, node) {
58
+ if (!(error instanceof XPathError) || error.location || !node) return error;
59
+ const location = locationOf(node);
60
+ error.location = location;
61
+ if (location.line !== undefined) {
62
+ const where = location.module ? ` of ${location.module}` : "";
63
+ // the message without location, for err:description in xsl:catch
64
+ error.plainMessage = error.message;
65
+ error.message += ` (line ${location.line}${where})`;
66
+ }
67
+ return error;
68
+ }
69
+
70
+ /**
71
+ * Adds to an error the location of the step that was running: the last
72
+ * step started by the innermost body frame.
73
+ * @param {*} error
74
+ * @param {object[]} frames - The work stack (see machine.js)
75
+ * @param {number} base - Frames below it belong to an outer driver
76
+ * @returns {*} the error
77
+ */
78
+ export function locateInFrames(error, frames, base) {
79
+ if (!(error instanceof XPathError) || error.location) return error;
80
+ for (let i = frames.length - 1; i >= base; i--) {
81
+ const { body, index } = frames[i];
82
+ const node = body && index > 0 ? stepNodes.get(body[index - 1]) : null;
83
+ if (node) return locate(error, node);
84
+ }
85
+ return error;
86
+ }
@@ -0,0 +1,137 @@
1
+ /**
2
+ * The explicit work stack that runs sequence constructors.
3
+ *
4
+ * Template rules nest as deep as the documents and recursions they
5
+ * process. Instantiating each level with JavaScript recursion costs
6
+ * several native frames per level and exhausts the call stack long before
7
+ * 10,000 levels, so instructions whose content comes last (the content of
8
+ * literal result elements, xsl:if, xsl:choose, template invocations, the
9
+ * iterations of xsl:for-each and xsl:apply-templates) push a frame and
10
+ * return; the driver runs the frame before the next instruction of the
11
+ * frame below. Instructions that need a result at once (variables,
12
+ * attributes, parameters, function calls) run a nested driver.
13
+ *
14
+ * @module @tradik/xslt3/xslt/runtime/machine
15
+ */
16
+
17
+ import { xsltError } from "../names.js";
18
+ import { locateInFrames } from "./locations.js";
19
+
20
+ /**
21
+ * A compiled sequence constructor: steps that are instructions
22
+ * `(xc, out, machine) => void` or variable bindings `{binding}`.
23
+ * @typedef {Array<Function|{binding: Function}>} Body
24
+ */
25
+
26
+ /** Runs the steps of a body with a context and a receiver. */
27
+ export class BodyFrame {
28
+ /**
29
+ * @param {Body} body
30
+ * @param {object} xc - XSLT context
31
+ * @param {object} out - Receiver
32
+ */
33
+ constructor(body, xc, out) {
34
+ this.body = body;
35
+ this.index = 0;
36
+ this.xc = xc;
37
+ this.out = out;
38
+ }
39
+
40
+ /**
41
+ * Runs steps until one pushes a frame or none is left.
42
+ * @param {Machine} machine
43
+ * @returns {boolean} false when the body is done
44
+ */
45
+ advance(machine) {
46
+ const body = this.body;
47
+ const frames = machine.frames;
48
+ const height = frames.length;
49
+ while (this.index < body.length) {
50
+ const step = body[this.index++];
51
+ if (step.binding) this.xc = step.binding(this.xc, machine);
52
+ else step(this.xc, this.out, machine);
53
+ if (frames.length !== height) return true;
54
+ }
55
+ return false;
56
+ }
57
+ }
58
+
59
+ /** Visits the items of a sequence one after the other. */
60
+ export class LoopFrame {
61
+ /**
62
+ * @param {number} count
63
+ * @param {(index: number, machine: Machine) => void} visit - May push
64
+ * frames, which run before the next visit
65
+ */
66
+ constructor(count, visit) {
67
+ this.count = count;
68
+ this.index = 0;
69
+ this.visit = visit;
70
+ }
71
+
72
+ /**
73
+ * @param {Machine} machine
74
+ * @returns {boolean} false when every item is visited
75
+ */
76
+ advance(machine) {
77
+ const frames = machine.frames;
78
+ const height = frames.length;
79
+ while (this.index < this.count) {
80
+ this.visit(this.index++, machine);
81
+ if (frames.length !== height) return true;
82
+ }
83
+ return false;
84
+ }
85
+ }
86
+
87
+ /** The work stack of a transformation. */
88
+ export class Machine {
89
+ /** @param {number} [maxDepth] - Deepest stack allowed */
90
+ constructor(maxDepth = 1e6) {
91
+ this.frames = [];
92
+ this.maxDepth = maxDepth;
93
+ }
94
+
95
+ /**
96
+ * Schedules a frame: it runs before the next step of the current one.
97
+ * @param {BodyFrame|LoopFrame} frame
98
+ */
99
+ push(frame) {
100
+ if (this.frames.length >= this.maxDepth) {
101
+ throw xsltError(
102
+ "XTDE0000",
103
+ "Too deep recursion: the stylesheet probably loops",
104
+ );
105
+ }
106
+ this.frames.push(frame);
107
+ }
108
+
109
+ /**
110
+ * Runs a frame and everything it schedules to completion.
111
+ * @param {BodyFrame|LoopFrame} frame
112
+ */
113
+ run(frame) {
114
+ const frames = this.frames;
115
+ const base = frames.length;
116
+ this.push(frame);
117
+ try {
118
+ while (frames.length > base) {
119
+ if (!frames[frames.length - 1].advance(this)) frames.pop();
120
+ }
121
+ } catch (error) {
122
+ locateInFrames(error, frames, base);
123
+ frames.length = base;
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * Runs a body to completion.
130
+ * @param {Body} body
131
+ * @param {object} xc
132
+ * @param {object} out
133
+ */
134
+ runBody(body, xc, out) {
135
+ if (body.length > 0) this.run(new BodyFrame(body, xc, out));
136
+ }
137
+ }
@@ -0,0 +1,167 @@
1
+ /**
2
+ * xsl:merge at run time (XSLT 3.0 sections 15.3 to 15.6): the merge
3
+ * input sequences with their merge key values, checked to be sorted (or
4
+ * sorted first), merged in a stable order, and partitioned into groups
5
+ * of equal composite keys; and the functions current-merge-group() and
6
+ * current-merge-key().
7
+ *
8
+ * @module @tradik/xslt3/xslt/runtime/merging
9
+ */
10
+
11
+ import { xsltError } from "../names.js";
12
+ import { withFocus } from "./context.js";
13
+ import { compareValues, keyValue, settingsOf } from "./sortKeys.js";
14
+
15
+ /**
16
+ * Compares two composite merge keys.
17
+ * @param {Array} a
18
+ * @param {Array} b
19
+ * @param {object[]} settings - Settings of each key component
20
+ * @param {object} xc
21
+ * @returns {number}
22
+ * @throws {import("../../errors.js").XPathError} XTTE2230 for values that
23
+ * cannot be compared
24
+ */
25
+ export function compareKeys(a, b, settings, xc) {
26
+ for (let k = 0; k < settings.length; k++) {
27
+ let result;
28
+ try {
29
+ result = compareValues(a[k], b[k], settings[k], xc);
30
+ } catch (error) {
31
+ if (error.code !== "XTDE1030") throw error;
32
+ throw xsltError("XTTE2230", "Merge key values are not comparable");
33
+ }
34
+ if (result !== 0) return settings[k].descending ? -result : result;
35
+ }
36
+ return 0;
37
+ }
38
+
39
+ /**
40
+ * The effective values of the attributes of the merge keys of a source,
41
+ * which must be the same for every source (XTDE2210).
42
+ * @param {object[]} keys - Compiled xsl:merge-key elements
43
+ * @param {object} xc
44
+ * @returns {string[]}
45
+ */
46
+ function effectiveAttributes(keys, xc) {
47
+ const names = ["order", "lang", "collation", "caseOrder", "dataType"];
48
+ return keys.map((key) =>
49
+ names
50
+ .map((name) => (key[name] ? `=${key[name](xc).trim()}` : ""))
51
+ .join("|"),
52
+ );
53
+ }
54
+
55
+ /**
56
+ * The entries of one merge input sequence: items with their keys, sorted
57
+ * or checked.
58
+ * @param {Array} items
59
+ * @param {object} source - Compiled xsl:merge-source
60
+ * @param {object[]} settings
61
+ * @param {object} xc
62
+ * @param {object} machine
63
+ * @returns {Array<{item: *, keys: Array}>}
64
+ */
65
+ function inputEntries(items, source, settings, xc, machine) {
66
+ const entries = items.map((item) => ({
67
+ item,
68
+ keys: source.keys.map((key, k) =>
69
+ keyValue(key, settings[k], withFocus(xc, item, 1, 1), machine),
70
+ ),
71
+ }));
72
+ if (source.sortBeforeMerge) {
73
+ return entries.sort((a, b) => compareKeys(a.keys, b.keys, settings, xc));
74
+ }
75
+ for (let i = 1; i < entries.length; i++) {
76
+ if (compareKeys(entries[i - 1].keys, entries[i].keys, settings, xc) > 0) {
77
+ throw xsltError("XTDE2220", "A merge input sequence is not sorted");
78
+ }
79
+ }
80
+ return entries;
81
+ }
82
+
83
+ /**
84
+ * Computes the groups of an xsl:merge.
85
+ * @param {object[]} sources - Compiled xsl:merge-source elements
86
+ * @param {object} xc - Context of the instruction
87
+ * @param {object} machine
88
+ * @returns {Array<{entries: object[], keys: Array}>} groups in order
89
+ */
90
+ export function mergeGroups(sources, xc, machine) {
91
+ const attributes = sources.map((s) => effectiveAttributes(s.keys, xc).join());
92
+ if (attributes.some((text) => text !== attributes[0])) {
93
+ throw xsltError("XTDE2210", "The merge keys of the sources differ");
94
+ }
95
+ const all = [];
96
+ let settings = null;
97
+ for (const source of sources) {
98
+ const sourceSettings = source.keys.map((key) => settingsOf(key, xc));
99
+ settings ??= sourceSettings;
100
+ for (const items of source.inputs(xc, machine)) {
101
+ for (const entry of inputEntries(
102
+ items,
103
+ source,
104
+ sourceSettings,
105
+ xc,
106
+ machine,
107
+ )) {
108
+ all.push({ ...entry, source: source.name });
109
+ }
110
+ }
111
+ }
112
+ if (all.length === 0) return [];
113
+ all.sort((a, b) => compareKeys(a.keys, b.keys, settings, xc));
114
+ const groups = [];
115
+ for (const entry of all) {
116
+ const last = groups.at(-1);
117
+ if (last && compareKeys(last.keys, entry.keys, settings, xc) === 0) {
118
+ last.entries.push(entry);
119
+ } else groups.push({ entries: [entry], keys: entry.keys });
120
+ }
121
+ return groups;
122
+ }
123
+
124
+ /**
125
+ * The current merge group, items of all sources or of one.
126
+ * @param {Array} args
127
+ * @param {object} context
128
+ * @returns {Array}
129
+ */
130
+ function currentMergeGroup(args, context) {
131
+ const merge = context.xc.merge;
132
+ if (!merge) throw xsltError("XTDE3480", "There is no current merge group");
133
+ if (args.length === 0) return merge.group.entries.map((e) => e.item);
134
+ const name = args[0][0].value;
135
+ if (!merge.names.includes(name)) {
136
+ throw xsltError("XTDE3490", `No merge source is named ${name}`);
137
+ }
138
+ return merge.group.entries
139
+ .filter((entry) => entry.source === name)
140
+ .map((entry) => entry.item);
141
+ }
142
+
143
+ /** Function definitions. */
144
+ export const mergeFunctions = [
145
+ {
146
+ local: "current-merge-group",
147
+ params: [],
148
+ returns: "item()*",
149
+ impl: currentMergeGroup,
150
+ },
151
+ {
152
+ local: "current-merge-group",
153
+ params: ["xs:string"],
154
+ returns: "item()*",
155
+ impl: currentMergeGroup,
156
+ },
157
+ {
158
+ local: "current-merge-key",
159
+ params: [],
160
+ returns: "xs:anyAtomicType*",
161
+ impl: (_, context) => {
162
+ const merge = context.xc.merge;
163
+ if (!merge) throw xsltError("XTDE3510", "There is no current merge key");
164
+ return merge.group.keys.filter((key) => key !== null);
165
+ },
166
+ },
167
+ ];