@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,110 @@
1
+ /**
2
+ * Conditional content construction at run time (XSLT 3.0 section 8.4):
3
+ * vacuous and deemed-empty items, the sequence constructors that contain
4
+ * xsl:on-empty or xsl:on-non-empty, and the filter of
5
+ * xsl:where-populated.
6
+ *
7
+ * @module @tradik/xslt3/xslt/runtime/conditional
8
+ */
9
+
10
+ import { isArray, isAtomic, isMap } from "../../xdm/atomic.js";
11
+ import { canonicalString } from "../../xdm/lexical.js";
12
+ import { SequenceReceiver } from "./sequenceReceiver.js";
13
+
14
+ /**
15
+ * The members of an array, flattened.
16
+ * @param {object} array
17
+ * @returns {Array}
18
+ */
19
+ const flatten = (array) =>
20
+ array.members
21
+ .flat()
22
+ .flatMap((item) => (isArray(item) ? flatten(item) : [item]));
23
+
24
+ /**
25
+ * Whether an item is vacuous: a zero-length text node, a document node
26
+ * without children, an atomic value whose string is empty, an array of
27
+ * vacuous items.
28
+ * @param {*} item
29
+ * @returns {boolean}
30
+ */
31
+ export function isVacuous(item) {
32
+ if (isAtomic(item)) return canonicalString(item) === "";
33
+ if (isArray(item)) return flatten(item).every(isVacuous);
34
+ const type = item?.nodeType;
35
+ if (type === 3 || type === 4) return item.nodeValue === "";
36
+ return (type === 9 || type === 11) && !item.firstChild;
37
+ }
38
+
39
+ /**
40
+ * Whether xsl:where-populated drops an item: a document or element node
41
+ * without children, another node whose string value is empty, an atomic
42
+ * value whose string is empty, an empty map, an array of such items.
43
+ * @param {*} item
44
+ * @returns {boolean}
45
+ */
46
+ export function isDeemedEmpty(item) {
47
+ if (isAtomic(item)) return canonicalString(item) === "";
48
+ if (isMap(item)) return item.size === 0;
49
+ if (isArray(item)) return flatten(item).every(isDeemedEmpty);
50
+ const type = item?.nodeType;
51
+ if (type === 1 || type === 9 || type === 11) return !item.firstChild;
52
+ if (type === undefined) return false;
53
+ const value = type === 2 ? item.value : item.nodeValue;
54
+ return (value ?? "") === "";
55
+ }
56
+
57
+ /**
58
+ * Runs a step and collects the items it produces.
59
+ * @param {Function} step
60
+ * @param {object} xc
61
+ * @param {import("./machine.js").Machine} machine
62
+ * @returns {Array}
63
+ */
64
+ export function stepItems(step, xc, machine) {
65
+ const out = new SequenceReceiver(xc.tx.scratch);
66
+ machine.runBody([step], xc, out);
67
+ return out.items;
68
+ }
69
+
70
+ /**
71
+ * Wraps a body that contains xsl:on-empty or xsl:on-non-empty (steps
72
+ * whose `conditional` is "empty" or "non-empty"): the other steps run
73
+ * first, collected; the xsl:on-non-empty steps run when one of them
74
+ * gave a non-vacuous item, the xsl:on-empty step when none did.
75
+ * @param {import("./machine.js").Body} body
76
+ * @returns {Function} a step running the whole body
77
+ */
78
+ export function conditionalBody(body) {
79
+ return (xc, out, machine) => {
80
+ const results = [];
81
+ let context = xc;
82
+ let populated = false;
83
+ let onEmpty = null;
84
+ for (const step of body) {
85
+ if (step.binding) {
86
+ context = step.binding(context, machine);
87
+ } else if (step.conditional === "empty") {
88
+ onEmpty = { step, xc: context };
89
+ } else if (step.conditional === "non-empty") {
90
+ results.push({ step, xc: context });
91
+ } else {
92
+ const items = stepItems(step, context, machine);
93
+ populated ||= !items.every(isVacuous);
94
+ results.push(items);
95
+ }
96
+ }
97
+ const items = [];
98
+ if (!populated && onEmpty) {
99
+ items.push(...stepItems(onEmpty.step, onEmpty.xc, machine));
100
+ } else {
101
+ for (const result of results) {
102
+ if (Array.isArray(result)) items.push(...result);
103
+ else if (populated) {
104
+ items.push(...stepItems(result.step, result.xc, machine));
105
+ }
106
+ }
107
+ }
108
+ for (const item of items) out.item(item);
109
+ };
110
+ }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * The XSLT context of an instruction (XSLT 3.0 section 5.4.3): the focus,
3
+ * the variables in scope, the current mode and template rule, the current
4
+ * group and grouping key, the captured groups of xsl:analyze-string, the
5
+ * tunnel parameters, and the transformation it belongs to. Contexts are
6
+ * immutable: instructions derive new ones.
7
+ *
8
+ * @module @tradik/xslt3/xslt/runtime/context
9
+ */
10
+
11
+ /**
12
+ * @typedef {object} XsltContext
13
+ * @property {object} tx - The transformation (see transformation.js)
14
+ * @property {*} item - Context item, undefined when absent
15
+ * @property {number} position
16
+ * @property {number} size
17
+ * @property {{value: Array, next: object}|null} env - Variable values
18
+ * @property {object|null} mode - Current mode
19
+ * @property {object|null} rule - Current template rule
20
+ * @property {Array|undefined} group - Current group
21
+ * @property {Array|undefined} groupKey - Current grouping key
22
+ * @property {string[]|undefined} regex - Captured groups
23
+ * @property {Map<string, Array>|null} tunnel - Tunnel parameters
24
+ * @property {boolean} temporary - Temporary output state
25
+ * @property {object|null} dyn - Dynamic context of XPath, made on demand
26
+ */
27
+
28
+ /**
29
+ * A context with some fields changed.
30
+ * @param {XsltContext} xc
31
+ * @param {Partial<XsltContext>} changes
32
+ * @returns {XsltContext}
33
+ */
34
+ export function derive(xc, changes) {
35
+ const next = { ...xc, ...changes };
36
+ next.dyn = null;
37
+ return next;
38
+ }
39
+
40
+ /**
41
+ * A context with a new focus.
42
+ * @param {XsltContext} xc
43
+ * @param {*} item
44
+ * @param {number} position
45
+ * @param {number} size
46
+ * @returns {XsltContext}
47
+ */
48
+ export const withFocus = (xc, item, position, size) =>
49
+ derive(xc, { item, position, size });
50
+
51
+ /**
52
+ * A context with a variable bound innermost.
53
+ * @param {XsltContext} xc
54
+ * @param {Array} value
55
+ * @returns {XsltContext}
56
+ */
57
+ export const withVariable = (xc, value) =>
58
+ derive(xc, { env: { value, next: xc.env } });
59
+
60
+ /**
61
+ * The XPath dynamic context of an XSLT context: the dynamic context of
62
+ * the transformation with the XSLT context attached (read by current(),
63
+ * current-group(), regex-group()...).
64
+ * @param {XsltContext} xc
65
+ * @returns {object}
66
+ */
67
+ export function dynamicContextOf(xc) {
68
+ let dyn = xc.dyn;
69
+ if (!dyn) {
70
+ dyn = Object.create(xc.tx.dyn);
71
+ dyn.xc = xc;
72
+ xc.dyn = dyn;
73
+ }
74
+ return dyn;
75
+ }
76
+
77
+ /**
78
+ * Evaluates a compiled expression in an XSLT context.
79
+ * @param {{run: Function}} expr
80
+ * @param {XsltContext} xc
81
+ * @returns {Array} the result sequence
82
+ */
83
+ export function evaluate(expr, xc) {
84
+ const dyn = dynamicContextOf(xc);
85
+ useStaticContext(dyn, expr.sc);
86
+ return expr.run({
87
+ item: xc.item,
88
+ position: xc.position,
89
+ size: xc.size,
90
+ env: xc.env,
91
+ dyn,
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Makes the static context of the expression about to run visible to
97
+ * the functions that need it (key(), system-property(), format-number(),
98
+ * doc() with a relative URI...).
99
+ * @param {object} dyn - XPath dynamic context
100
+ * @param {object} sc - Static context of the expression
101
+ */
102
+ export function useStaticContext(dyn, sc) {
103
+ dyn.sc = sc;
104
+ dyn.staticBaseUri = sc.baseUri;
105
+ dyn.decimalFormats = sc.decimalFormats;
106
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Deep copies of nodes into a receiver (xsl:copy-of, nodes added to
3
+ * element content), with or without their namespace nodes. Iterative, so
4
+ * deep trees do not exhaust the call stack.
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/copy
7
+ */
8
+
9
+ import {
10
+ attributesOf,
11
+ isNamespaceDeclaration,
12
+ } from "../../xpath/eval/domNodes.js";
13
+ import { inScopeNamespaces } from "../../xpath/eval/namespaceNodes.js";
14
+ import { markCopied, markDocumentCopied } from "./baseUri.js";
15
+
16
+ /**
17
+ * Copies the namespace nodes of an element into a receiver.
18
+ * @param {Element} element
19
+ * @param {object} receiver
20
+ */
21
+ export function copyNamespaceNodes(element, receiver) {
22
+ for (const [prefix, uri] of inScopeNamespaces(element)) {
23
+ if (prefix !== "xml" && uri !== "") receiver.namespace(prefix, uri);
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Starts the copy of an element: its name, namespaces and attributes.
29
+ * @param {Element} element
30
+ * @param {object} receiver
31
+ * @param {boolean} copyNamespaces
32
+ * @param {boolean} [top] - The element is the root of the copy (its
33
+ * inherited namespaces are copied too)
34
+ * @returns {object} the receiver of the element's content
35
+ */
36
+ export function startElementCopy(
37
+ element,
38
+ receiver,
39
+ copyNamespaces,
40
+ top = true,
41
+ ) {
42
+ const content = receiver.element(
43
+ element.namespaceURI ?? "",
44
+ element.nodeName,
45
+ );
46
+ if (top) markCopied(element, content.parent);
47
+ if (copyNamespaces && top) copyNamespaceNodes(element, content);
48
+ else if (copyNamespaces) {
49
+ for (const attribute of element.attributes) {
50
+ if (!isNamespaceDeclaration(attribute)) continue;
51
+ const name = attribute.name;
52
+ content.namespace(name === "xmlns" ? "" : name.slice(6), attribute.value);
53
+ }
54
+ }
55
+ for (const attribute of attributesOf(element)) {
56
+ content.attribute(
57
+ attribute.namespaceURI ?? "",
58
+ attribute.nodeName,
59
+ attribute.value,
60
+ );
61
+ }
62
+ return content;
63
+ }
64
+
65
+ /**
66
+ * Copies a node that is not an element or document.
67
+ * @param {Node} node
68
+ * @param {object} receiver
69
+ */
70
+ export function copyLeaf(node, receiver) {
71
+ switch (node.nodeType) {
72
+ case 2:
73
+ receiver.attribute(node.namespaceURI ?? "", node.nodeName, node.value);
74
+ break;
75
+ case 3:
76
+ case 4:
77
+ receiver.text(node.nodeValue);
78
+ break;
79
+ case 7:
80
+ receiver.pi(node.target ?? node.nodeName, node.nodeValue);
81
+ break;
82
+ case 8:
83
+ receiver.comment(node.nodeValue);
84
+ break;
85
+ default:
86
+ // a namespace node
87
+ receiver.namespace(node.localName, node.nodeValue);
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Copies a node with its descendants into a receiver; a document node
93
+ * contributes its children.
94
+ * @param {Node} node
95
+ * @param {object} receiver - Tree or sequence receiver
96
+ * @param {boolean} copyNamespaces - Copy the namespace nodes of elements
97
+ */
98
+ export function copyNode(node, receiver, copyNamespaces) {
99
+ const stack = [[node, receiver]];
100
+ while (stack.length > 0) {
101
+ const [current, target] = stack.pop();
102
+ const type = current.nodeType;
103
+ let content;
104
+ if (type === 1) {
105
+ content = startElementCopy(
106
+ current,
107
+ target,
108
+ copyNamespaces,
109
+ current === node,
110
+ );
111
+ } else if (type === 9 || type === 11) {
112
+ content = target.document();
113
+ markDocumentCopied(current, content);
114
+ } else {
115
+ copyLeaf(current, target);
116
+ continue;
117
+ }
118
+ const children = current.childNodes;
119
+ for (let i = children.length - 1; i >= 0; i--) {
120
+ if (children[i].nodeType !== 10) stack.push([children[i], content]);
121
+ }
122
+ }
123
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * fn:copy-of and fn:snapshot (XSLT 3.0 sections 20.6 and 20.7): deep
3
+ * copies of nodes; a snapshot also copies the ancestors of each node.
4
+ *
5
+ * @module @tradik/xslt3/xslt/runtime/copyFunctions
6
+ */
7
+
8
+ import { isNode } from "../../xdm/atomic.js";
9
+ import { xsltError } from "../names.js";
10
+ import { setOrigin } from "./accumulators.js";
11
+ import { SequenceReceiver } from "./sequenceReceiver.js";
12
+ import { snapshotNode } from "./snapshot.js";
13
+
14
+ /**
15
+ * Copies the nodes of a sequence; other items are kept.
16
+ * @param {Array} items
17
+ * @param {object} context - XPath dynamic context
18
+ * @param {boolean} snapshot - fn:snapshot: copy the ancestors too
19
+ * @returns {Array}
20
+ */
21
+ function copyItems(items, context, snapshot) {
22
+ const result = [];
23
+ for (const item of items) {
24
+ if (!isNode(item)) {
25
+ result.push(item);
26
+ continue;
27
+ }
28
+ const out = new SequenceReceiver(context.xc.tx.scratch);
29
+ let copy;
30
+ if (snapshot) {
31
+ copy = snapshotNode(item, out, (c, o) => setOrigin(context.xc.tx, c, o));
32
+ } else {
33
+ out.copy(item, true);
34
+ copy = out.items.at(-1);
35
+ }
36
+ setOrigin(context.xc.tx, copy, item);
37
+ result.push(copy);
38
+ }
39
+ return result;
40
+ }
41
+
42
+ /**
43
+ * The context item of a focus function.
44
+ * @param {object} context
45
+ * @returns {Array}
46
+ * @throws {import("../../errors.js").XPathError} XPDY0002 when absent
47
+ */
48
+ function contextItemOf(context) {
49
+ if (context.contextItem === undefined) {
50
+ throw xsltError("XPDY0002", "The context item is absent");
51
+ }
52
+ return [context.contextItem];
53
+ }
54
+
55
+ /** Function definitions. */
56
+ export const copyFunctions = ["copy-of", "snapshot"].flatMap((local) => [
57
+ {
58
+ local,
59
+ params: [],
60
+ returns: "item()*",
61
+ focus: true,
62
+ impl: (_, context) =>
63
+ copyItems(contextItemOf(context), context, local === "snapshot"),
64
+ },
65
+ {
66
+ local,
67
+ params: ["item()*"],
68
+ returns: "item()*",
69
+ impl: ([items], context) => copyItems(items, context, local === "snapshot"),
70
+ },
71
+ ]);
@@ -0,0 +1,60 @@
1
+ /**
2
+ * The original node a node of a copy stands for, for the accumulator
3
+ * values of copies (fn:copy-of, fn:snapshot, xsl:copy-of with
4
+ * copy-accumulators): copied nodes are recorded with their original, and
5
+ * a node inside a recorded copy is found by its child positions.
6
+ *
7
+ * Every accumulator-before()/accumulator-after() call asks, so the common
8
+ * case (a node of a source tree, in no copy) must stay cheap: the
9
+ * ancestors are first searched for a recorded copy, and child positions
10
+ * are only computed for nodes that are inside one. Positions are counted
11
+ * along previousSibling rather than with an indexOf over childNodes,
12
+ * which on jsdom goes through a live NodeList proxy per index.
13
+ *
14
+ * @module @tradik/xslt3/xslt/runtime/copyOrigins
15
+ */
16
+
17
+ /**
18
+ * Position of a node among the children of its parent.
19
+ * @param {Node} node
20
+ * @returns {number}
21
+ */
22
+ function positionOf(node) {
23
+ let position = 0;
24
+ for (let s = node.previousSibling; s; s = s.previousSibling) position++;
25
+ return position;
26
+ }
27
+
28
+ /**
29
+ * The nearest recorded copy containing a node (the node itself included).
30
+ * @param {WeakMap<Node, Node>} origins
31
+ * @param {Node} node
32
+ * @returns {Node|null}
33
+ */
34
+ function recordedAncestor(origins, node) {
35
+ for (let current = node; current; current = current.parentNode) {
36
+ if (origins.has(current)) return current;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ /**
42
+ * The node of the original tree a node of a copy stands for.
43
+ * @param {WeakMap<Node, Node>} origins - Copied nodes to original nodes
44
+ * @param {Node} node - Not an attribute (accumulators have no values
45
+ * for attributes)
46
+ * @returns {Node|null} the original, null when the node is not in a copy
47
+ */
48
+ export function originalNode(origins, node) {
49
+ const copy = recordedAncestor(origins, node);
50
+ if (!copy) return null;
51
+ const path = [];
52
+ for (let current = node; current !== copy; current = current.parentNode) {
53
+ path.push(positionOf(current));
54
+ }
55
+ let original = origins.get(copy);
56
+ for (let i = path.length - 1; i >= 0; i--) {
57
+ original = original.childNodes[path[i]];
58
+ }
59
+ return original;
60
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The default output method of a result tree (XSLT 3.0 section 26.1): html
3
+ * for an `html` document element in no namespace (any case), xhtml for
4
+ * `html` in the XHTML namespace (xml when the principal stylesheet module
5
+ * is version 1.0 and the tree is the implicit principal result), else xml.
6
+ *
7
+ * @module @tradik/xslt3/xslt/runtime/defaultMethod
8
+ */
9
+
10
+ const XHTML_NS = "http://www.w3.org/1999/xhtml";
11
+
12
+ /**
13
+ * The method a tree implies.
14
+ * @param {*} tree - Document or document fragment (other values: xml)
15
+ * @param {boolean} versionOne - XSLT 1.0 rules for an XHTML tree
16
+ * @returns {string}
17
+ */
18
+ function impliedMethod(tree, versionOne) {
19
+ for (let child = tree?.firstChild; child; child = child.nextSibling) {
20
+ if (child.nodeType === 3 && /\S/.test(child.nodeValue)) return "xml";
21
+ if (child.nodeType !== 1) continue;
22
+ const local = child.localName;
23
+ const uri = child.namespaceURI ?? "";
24
+ if (local.toLowerCase() === "html" && uri === "") return "html";
25
+ if (local === "html" && uri === XHTML_NS && !versionOne) return "xhtml";
26
+ return "xml";
27
+ }
28
+ return "xml";
29
+ }
30
+
31
+ /**
32
+ * Serialization parameters with the default method when they have none.
33
+ * @param {object} output - Parameters of the output definition
34
+ * @param {*} tree - The result
35
+ * @param {boolean} [versionOne] - The implicit principal result of a
36
+ * version 1.0 stylesheet
37
+ * @returns {object}
38
+ */
39
+ export function withDefaultMethod(output, tree, versionOne = false) {
40
+ if (output.method !== undefined) return output;
41
+ return { ...output, method: impliedMethod(tree, versionOne) };
42
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * The content of a document node added to a tree (XSLT 3.0 section
3
+ * 5.7.1): its children go to the enclosing node, but attributes and
4
+ * namespaces are errors (XTDE0420).
5
+ *
6
+ * @module @tradik/xslt3/xslt/runtime/documentContent
7
+ */
8
+
9
+ import { isAtomic } from "../../xdm/atomic.js";
10
+ import { canonicalString } from "../../xdm/lexical.js";
11
+ import { xsltError } from "../names.js";
12
+ import { copyNode } from "./copy.js";
13
+
14
+ /** Receives the content of a document node inside a tree. */
15
+ export class DocumentContent {
16
+ /** @param {object} target - Receiver of the enclosing node's content */
17
+ constructor(target) {
18
+ this.target = target;
19
+ this.lastAtomic = false;
20
+ }
21
+
22
+ /**
23
+ * @param {string} uri
24
+ * @param {string} qname
25
+ * @returns {object} the receiver of the new element's content
26
+ */
27
+ element(uri, qname) {
28
+ this.lastAtomic = false;
29
+ return this.target.element(uri, qname);
30
+ }
31
+
32
+ /** Raises XTDE0420. */
33
+ attribute() {
34
+ throw xsltError("XTDE0420", "A document node cannot have attributes");
35
+ }
36
+
37
+ /** Raises XTDE0420. */
38
+ namespace() {
39
+ throw xsltError("XTDE0420", "A document node cannot have namespaces");
40
+ }
41
+
42
+ /** @param {string} text */
43
+ text(text) {
44
+ this.lastAtomic = false;
45
+ this.target.text(text);
46
+ }
47
+
48
+ /** @param {string} text */
49
+ comment(text) {
50
+ this.lastAtomic = false;
51
+ this.target.comment(text);
52
+ }
53
+
54
+ /**
55
+ * @param {string} target
56
+ * @param {string} data
57
+ */
58
+ pi(target, data) {
59
+ this.lastAtomic = false;
60
+ this.target.pi(target, data);
61
+ }
62
+
63
+ /** @param {*} value - An atomic value */
64
+ atomic(value) {
65
+ const text = canonicalString(value);
66
+ this.target.text(this.lastAtomic ? ` ${text}` : text);
67
+ this.lastAtomic = true;
68
+ }
69
+
70
+ /** @param {*} item */
71
+ item(item) {
72
+ if (isAtomic(item)) this.atomic(item);
73
+ else if (typeof item?.nodeType === "number") this.copy(item, true);
74
+ else {
75
+ this.lastAtomic = false;
76
+ this.target.item(item);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * @param {Node} node
82
+ * @param {boolean} copyNamespaces
83
+ */
84
+ copy(node, copyNamespaces) {
85
+ copyNode(node, this, copyNamespaces);
86
+ }
87
+
88
+ /** @returns {DocumentContent} itself */
89
+ document() {
90
+ this.lastAtomic = false;
91
+ return this;
92
+ }
93
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * The target expressions of xsl:evaluate (XSLT 3.0 sections 10.4.1 and
3
+ * 10.4.2): compiled at run time against a static context built from the
4
+ * instruction (or from the namespace-context node), without the
5
+ * XSLT-defined functions and the stylesheet variables, and cached per
6
+ * instruction.
7
+ *
8
+ * @module @tradik/xslt3/xslt/runtime/dynamicXPath
9
+ */
10
+
11
+ import { FunctionLibrary } from "../../functions/registry.js";
12
+ import { compileNode, withinLimits } from "../../xpath/eval/compiler.js";
13
+ import { inScopeNamespaces } from "../../xpath/eval/namespaceNodes.js";
14
+ import { createStaticContext } from "../../xpath/eval/staticContext.js";
15
+ import { parseXPath } from "../../xpath/syntax/index.js";
16
+ import { xsltError } from "../names.js";
17
+ import { xsltFunctions } from "./functions.js";
18
+
19
+ /** @type {WeakMap<FunctionLibrary, FunctionLibrary>} */
20
+ const libraries = new WeakMap();
21
+
22
+ /**
23
+ * The functions of a target expression: those of the stylesheet less
24
+ * the XSLT-defined ones (key(), current-group(), system-property()...).
25
+ * @param {FunctionLibrary} library - The library of the stylesheet
26
+ * @returns {FunctionLibrary}
27
+ */
28
+ function targetLibrary(library) {
29
+ let result = libraries.get(library);
30
+ if (!result) {
31
+ const xslt = new Set(xsltFunctions);
32
+ const hidden = (d) =>
33
+ d.visibility === "private" || d.visibility === "hidden";
34
+ result = new FunctionLibrary(
35
+ library.definitions.filter((d) => !xslt.has(d) && !hidden(d)),
36
+ );
37
+ libraries.set(library, result);
38
+ }
39
+ return result;
40
+ }
41
+
42
+ /**
43
+ * The namespaces of a namespace-context node: its in-scope namespaces,
44
+ * the default one as the default element namespace.
45
+ * @param {Node} node
46
+ * @returns {{namespaces: Map<string, string>, defaultElementNamespace: string}}
47
+ */
48
+ export function nodeNamespaces(node) {
49
+ const bindings = node.nodeType === 1 ? inScopeNamespaces(node) : new Map();
50
+ const defaultElementNamespace = bindings.get("") ?? "";
51
+ bindings.delete("");
52
+ return { namespaces: bindings, defaultElementNamespace };
53
+ }
54
+
55
+ /**
56
+ * Compiles a target expression.
57
+ * @param {string} text
58
+ * @param {object} context - `{namespaces, defaultElementNamespace,
59
+ * baseUri, library, decimalFormats}`
60
+ * @param {string[]} keys - Clark names of the variables, outermost first
61
+ * @returns {(ctx: object) => Array} the evaluator
62
+ * @throws {import("../../errors.js").XPathError} XTDE3160 for a static
63
+ * error in the expression
64
+ */
65
+ export function compileTarget(text, context, keys) {
66
+ try {
67
+ const sc = createStaticContext(
68
+ {
69
+ namespaces: context.namespaces,
70
+ defaultElementNamespace: context.defaultElementNamespace,
71
+ baseUri: context.baseUri,
72
+ decimalFormats: context.decimalFormats,
73
+ },
74
+ targetLibrary(context.library),
75
+ );
76
+ let vars = null;
77
+ for (const key of keys) vars = { key, next: vars };
78
+ const run = withinLimits(() => compileNode(parseXPath(text), { sc, vars }));
79
+ return Object.assign(run, { sc });
80
+ } catch (error) {
81
+ // an undeclared variable keeps its code: the parameters vary per call
82
+ if (typeof error?.code !== "string" || error.code === "XPST0008") {
83
+ throw error;
84
+ }
85
+ throw xsltError(
86
+ "XTDE3160",
87
+ `Invalid expression "${text}" in xsl:evaluate: ${error.message}`,
88
+ );
89
+ }
90
+ }