@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,154 @@
1
+ /**
2
+ * Compilation of sequence constructors (XSLT 3.0 section 5.7): each child
3
+ * of a template body, a literal result element or an instruction becomes
4
+ * a step of the body (see runtime/machine.js). Local variables extend the
5
+ * scope of their following siblings.
6
+ *
7
+ * @module @tradik/xslt3/xslt/compiler/body
8
+ */
9
+
10
+ import { avtEvaluator } from "../runtime/values.js";
11
+ import { isXsl, XSL_NS, xsltError } from "../names.js";
12
+ import { infoOf } from "./elementInfo.js";
13
+ import { instructionCompilers } from "../instructions/index.js";
14
+ import { compileLiteralElement } from "../instructions/literal.js";
15
+ import { compileLocalVariable } from "../instructions/variables.js";
16
+ import { checkAttributes } from "./attributes.js";
17
+ import { conditionalBody } from "../runtime/conditional.js";
18
+ import { locate, recordStep } from "../runtime/locations.js";
19
+
20
+ /**
21
+ * The scope of a compilation: the variables in scope (a linked list of
22
+ * Clark names, see xpath/eval/scope.js) and flags of the context.
23
+ * @typedef {object} Scope
24
+ * @property {{key: string, next: object}|null} vars
25
+ * @property {boolean} [inFunction] - In a stylesheet function
26
+ */
27
+
28
+ /**
29
+ * Compiles a text child: literal text, or a text value template under
30
+ * expand-text="yes".
31
+ * @param {{nodeValue: string, parentNode: Element}} text
32
+ * @param {object} cx - Stylesheet compiler
33
+ * @param {Scope} scope
34
+ * @returns {Function} the step
35
+ */
36
+ function compileText(text, cx, scope) {
37
+ const parent = text.parentNode;
38
+ const value = text.nodeValue;
39
+ if (infoOf(parent).expandText) {
40
+ const evaluator = avtEvaluator(cx.exprs.avt(value, parent, scope.vars));
41
+ return (xc, out) => out.text(evaluator(xc));
42
+ }
43
+ return (xc, out) => out.text(value);
44
+ }
45
+
46
+ /**
47
+ * Steps of an element whose instruction is not available: its
48
+ * xsl:fallback children, or a dynamic error when it has none.
49
+ * @param {Element} element
50
+ * @param {object} cx
51
+ * @param {Scope} scope
52
+ * @returns {Function|null}
53
+ */
54
+ export function compileFallback(element, cx, scope) {
55
+ const fallbacks = cx
56
+ .children(element)
57
+ .filter((child) => child.nodeType === 1 && isXsl(child, "fallback"));
58
+ if (fallbacks.length === 0) {
59
+ const name = element.nodeName;
60
+ if (element.namespaceURI === XSL_NS) {
61
+ throw xsltError("XTSE0010", `Unknown XSLT element ${name}`);
62
+ }
63
+ return () => {
64
+ throw xsltError("XTDE1450", `The instruction ${name} is not available`);
65
+ };
66
+ }
67
+ const bodies = fallbacks.map((fallback) => compileBody(fallback, cx, scope));
68
+ return (xc, out, machine) => {
69
+ for (const body of bodies) machine.runBody(body, xc, out);
70
+ };
71
+ }
72
+
73
+ /**
74
+ * Compiles an XSLT instruction.
75
+ * @param {Element} element
76
+ * @param {object} cx
77
+ * @param {Scope} scope
78
+ * @returns {Function|null} the step, null for none
79
+ */
80
+ function compileInstruction(element, cx, scope) {
81
+ const name = element.localName;
82
+ const compiler = instructionCompilers[name];
83
+ if (compiler) {
84
+ checkAttributes(element, cx);
85
+ return compiler(element, cx, scope);
86
+ }
87
+ if (name === "fallback") {
88
+ checkAttributes(element, cx);
89
+ return null;
90
+ }
91
+ if (infoOf(element).version > 3) return compileFallback(element, cx, scope);
92
+ if (name === "include" || name === "import") {
93
+ throw xsltError(
94
+ name === "include" ? "XTSE0170" : "XTSE0190",
95
+ `xsl:${name} is only allowed at the top level`,
96
+ );
97
+ }
98
+ throw xsltError(
99
+ "XTSE0010",
100
+ `xsl:${name} is not allowed in a sequence constructor`,
101
+ );
102
+ }
103
+
104
+ /**
105
+ * Compiles a child of a sequence constructor other than xsl:variable.
106
+ * @param {Node} child - Text or element
107
+ * @param {object} cx
108
+ * @param {Scope} scope
109
+ * @returns {Function|null} the step, null for none
110
+ */
111
+ function compileChild(child, cx, scope) {
112
+ if (child.nodeType === 3) return compileText(child, cx, scope);
113
+ if (child.namespaceURI === XSL_NS) {
114
+ return compileInstruction(child, cx, scope);
115
+ }
116
+ if (infoOf(child).extension.has(child.namespaceURI)) {
117
+ return compileFallback(child, cx, scope);
118
+ }
119
+ return compileLiteralElement(child, cx, scope);
120
+ }
121
+
122
+ /**
123
+ * Compiles the children of a stylesheet element as a sequence
124
+ * constructor.
125
+ * @param {Element} parent
126
+ * @param {object} cx - Stylesheet compiler
127
+ * @param {Scope} scope
128
+ * @param {Node[]} [children] - The children to compile (default: all)
129
+ * @returns {import("../runtime/machine.js").Body}
130
+ */
131
+ export function compileBody(parent, cx, scope, children) {
132
+ const body = [];
133
+ let current = scope;
134
+ for (const child of children ?? cx.children(parent)) {
135
+ try {
136
+ if (child.nodeType === 1 && isXsl(child, "variable")) {
137
+ checkAttributes(child, cx);
138
+ const compiled = compileLocalVariable(child, cx, current);
139
+ recordStep(compiled.step, child);
140
+ body.push(compiled.step);
141
+ current = compiled.scope;
142
+ continue;
143
+ }
144
+ const step = compileChild(child, cx, current);
145
+ recordStep(step, child);
146
+ if (step) body.push(step);
147
+ } catch (error) {
148
+ throw locate(error, child);
149
+ }
150
+ }
151
+ // xsl:on-empty and xsl:on-non-empty make the whole body conditional
152
+ const conditional = body.some((step) => step.conditional !== undefined);
153
+ return conditional ? [conditionalBody(body)] : body;
154
+ }
@@ -0,0 +1,105 @@
1
+ /**
2
+ * The children of a stylesheet element as the compiler sees them:
3
+ * comments and processing instructions dropped, adjacent text merged,
4
+ * whitespace-only text stripped (except in xsl:text and under
5
+ * xml:space="preserve"), and elements excluded by use-when left out
6
+ * (XSLT 3.0 sections 3.13.1 and 4.2).
7
+ *
8
+ * @module @tradik/xslt3/xslt/compiler/children
9
+ */
10
+
11
+ import { isXsl, XML_NS, XSL_NS } from "../names.js";
12
+
13
+ const WHITESPACE = /^[ \t\r\n]*$/;
14
+
15
+ /** XSLT elements whose content has no text: whitespace there is ignored. */
16
+ const ELEMENT_ONLY = new Set([
17
+ "stylesheet",
18
+ "transform",
19
+ "package",
20
+ "use-package",
21
+ "override",
22
+ "apply-templates",
23
+ "apply-imports",
24
+ "next-match",
25
+ "call-template",
26
+ "choose",
27
+ "attribute-set",
28
+ "character-map",
29
+ "analyze-string",
30
+ ]);
31
+
32
+ /**
33
+ * @param {object} node
34
+ * @returns {boolean} whether a child is whitespace-only text
35
+ */
36
+ export const isWhitespaceText = (node) =>
37
+ node.nodeType === 3 && WHITESPACE.test(node.nodeValue);
38
+
39
+ /**
40
+ * Splits leading children of one XSLT kind (xsl:param, xsl:sort) from the
41
+ * rest, ignoring whitespace text between them.
42
+ * @param {Array<object>} children
43
+ * @param {string} local - Local name of the leading elements
44
+ * @returns {{leading: Element[], rest: Array<object>}}
45
+ */
46
+ export function splitLeading(children, local) {
47
+ const leading = [];
48
+ let end = 0;
49
+ for (let i = 0; i < children.length; i++) {
50
+ if (isXsl(children[i], local)) {
51
+ leading.push(children[i]);
52
+ end = i + 1;
53
+ } else if (!isWhitespaceText(children[i])) break;
54
+ }
55
+ return { leading, rest: children.slice(end) };
56
+ }
57
+
58
+ /**
59
+ * @param {Element} element
60
+ * @returns {boolean} whether xml:space="preserve" applies to the children
61
+ */
62
+ function preservesSpace(element) {
63
+ for (let e = element; e && e.nodeType === 1; e = e.parentNode) {
64
+ const space = e.getAttributeNS(XML_NS, "space");
65
+ if (space) return space === "preserve";
66
+ }
67
+ return false;
68
+ }
69
+
70
+ /**
71
+ * A text child of a stylesheet element: `{nodeType: 3, nodeValue}`.
72
+ * @typedef {{nodeType: 3, nodeValue: string, parentNode: Element}} TextChild
73
+ */
74
+
75
+ /**
76
+ * The significant children of a stylesheet element.
77
+ * @param {Element} element
78
+ * @param {(element: Element) => boolean} included - Whether an element
79
+ * passes its use-when condition
80
+ * @returns {Array<Element|TextChild>} elements and texts
81
+ */
82
+ export function significantChildren(element, included) {
83
+ const elementOnly =
84
+ element.namespaceURI === XSL_NS && ELEMENT_ONLY.has(element.localName);
85
+ const keepSpace =
86
+ isXsl(element, "text") || (!elementOnly && preservesSpace(element));
87
+ const result = [];
88
+ let text = null;
89
+ const flush = () => {
90
+ if (text !== null && (keepSpace || !WHITESPACE.test(text))) {
91
+ result.push({ nodeType: 3, nodeValue: text, parentNode: element });
92
+ }
93
+ text = null;
94
+ };
95
+ for (let child = element.firstChild; child; child = child.nextSibling) {
96
+ const type = child.nodeType;
97
+ if (type === 3 || type === 4) text = (text ?? "") + child.nodeValue;
98
+ else if (type === 1) {
99
+ flush();
100
+ if (included(child)) result.push(child);
101
+ }
102
+ }
103
+ flush();
104
+ return result;
105
+ }
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Compatible signatures of an overriding and an overridden component
3
+ * (XSLT 3.0 section 3.5.3.3, XTSE3070): identical declared types of
4
+ * functions, variables and templates, and the template parameters an
5
+ * overriding template must keep.
6
+ *
7
+ * @module @tradik/xslt3/xslt/compiler/compatibility
8
+ */
9
+
10
+ import { attr, clarkOf, isXsl, xsltError } from "../names.js";
11
+ import { yesNo } from "./attributes.js";
12
+ import { infoOf } from "./elementInfo.js";
13
+
14
+ const QNAME = /([\p{L}_][\p{L}\p{N}_.-]*):([\p{L}_][\p{L}\p{N}_.-]*)/gu;
15
+
16
+ /**
17
+ * A declared type in a form that compares equal for identical types
18
+ * written with different prefixes or spacing.
19
+ * @param {Element} element - Declaration with the `as` attribute
20
+ * @returns {string}
21
+ */
22
+ function typeOf(element) {
23
+ const namespaces = infoOf(element).namespaces;
24
+ return (attr(element, "as") ?? "item()*")
25
+ .replace(/\s+/g, "")
26
+ .replace(QNAME, (name, prefix, local) =>
27
+ namespaces.has(prefix) ? `Q{${namespaces.get(prefix)}}${local}` : name,
28
+ );
29
+ }
30
+
31
+ /**
32
+ * The xsl:param children of a template or function.
33
+ * @param {Element} element
34
+ * @param {object} cx
35
+ * @returns {Array<{key: string, type: string, required: boolean, tunnel:
36
+ * boolean}>}
37
+ */
38
+ function paramsOf(element, cx) {
39
+ return cx
40
+ .children(element)
41
+ .filter((child) => isXsl(child, "param"))
42
+ .map((param) => ({
43
+ key: clarkOf(cx.exprs.qname(attr(param, "name"), param)),
44
+ type: typeOf(param),
45
+ required: yesNo(param, "required", false),
46
+ tunnel: yesNo(param, "tunnel", false),
47
+ }));
48
+ }
49
+
50
+ /**
51
+ * The xsl:context-item declaration of a template, as `use|type`.
52
+ * @param {Element} element
53
+ * @param {object} cx
54
+ * @returns {string}
55
+ */
56
+ function contextItemOf(element, cx) {
57
+ const item = cx.children(element).find((c) => isXsl(c, "context-item"));
58
+ if (!item) return "optional|item()";
59
+ const type = attr(item, "as") === undefined ? "item()" : typeOf(item);
60
+ return `${attr(item, "use")?.trim() ?? "optional"}|${type}`;
61
+ }
62
+
63
+ /**
64
+ * Whether an overriding template keeps the interface of the overridden
65
+ * one.
66
+ * @param {Element} overriding
67
+ * @param {Element} overridden
68
+ * @param {object} cx
69
+ * @returns {boolean}
70
+ */
71
+ function templatesCompatible(overriding, overridden, cx) {
72
+ const mine = new Map(paramsOf(overriding, cx).map((p) => [p.key, p]));
73
+ const theirs = paramsOf(overridden, cx);
74
+ for (const param of theirs) {
75
+ const other = mine.get(param.key);
76
+ if (param.tunnel) {
77
+ if (other && (!other.tunnel || other.type !== param.type)) return false;
78
+ } else if (
79
+ !other ||
80
+ other.tunnel ||
81
+ other.type !== param.type ||
82
+ other.required !== param.required
83
+ ) {
84
+ return false;
85
+ }
86
+ }
87
+ const known = new Set(theirs.map((p) => p.key));
88
+ return (
89
+ contextItemOf(overriding, cx) === contextItemOf(overridden, cx) &&
90
+ [...mine.values()].every((p) => known.has(p.key) || !p.required)
91
+ );
92
+ }
93
+
94
+ /**
95
+ * @param {Element} element - xsl:function
96
+ * @returns {string} its new-each-time value
97
+ */
98
+ const newEachTime = (element) =>
99
+ ({ true: "yes", 1: "yes", false: "no", 0: "no" })[
100
+ attr(element, "new-each-time")?.trim()
101
+ ] ??
102
+ attr(element, "new-each-time")?.trim() ??
103
+ "maybe";
104
+
105
+ /**
106
+ * XTSE3070: checks that an overriding declaration is compatible with the
107
+ * component it overrides.
108
+ * @param {Element} overriding
109
+ * @param {object} component - The overridden component (`kind`, `element`)
110
+ * @param {object} cx - The using package's compiler
111
+ */
112
+ export function checkCompatible(overriding, component, cx) {
113
+ const overridden = component.element;
114
+ let compatible = typeOf(overriding) === typeOf(overridden);
115
+ if (component.kind === "function") {
116
+ const mine = paramsOf(overriding, cx).map((p) => p.type);
117
+ const theirs = paramsOf(overridden, cx).map((p) => p.type);
118
+ compatible &&=
119
+ mine.every((type, i) => type === theirs[i]) &&
120
+ newEachTime(overriding) === newEachTime(overridden);
121
+ } else if (component.kind === "template") {
122
+ compatible &&= templatesCompatible(overriding, overridden, cx);
123
+ }
124
+ if (!compatible) {
125
+ throw xsltError(
126
+ "XTSE3070",
127
+ `The override of ${component.local} is incompatible`,
128
+ );
129
+ }
130
+ }
@@ -0,0 +1,179 @@
1
+ /**
2
+ * The named components a package declares (XSLT 3.0 section 3.5.3):
3
+ * named templates, functions, global variables and parameters,
4
+ * attribute sets and named modes, with their visibility from the
5
+ * visibility attribute and the xsl:expose declarations.
6
+ *
7
+ * @module @tradik/xslt3/xslt/compiler/components
8
+ */
9
+
10
+ import { attr, clarkOf, isXsl, xsltError } from "../names.js";
11
+ import { required } from "./attributes.js";
12
+ import { checkUnmatched, EXPLICIT, matchesOf } from "./visibility.js";
13
+
14
+ const DECLARED = new Set(["public", "private", "final", "abstract"]);
15
+ const MODE_DECLARED = new Set(["public", "private", "final"]);
16
+
17
+ /** Declared visibilities each explicitly exposed visibility allows. */
18
+ const EXPOSABLE = {
19
+ public: ["public"],
20
+ private: ["public", "private", "final"],
21
+ final: ["public", "final"],
22
+ abstract: ["abstract"],
23
+ };
24
+
25
+ /**
26
+ * The visibility attribute of a declaration.
27
+ * @param {Element} element
28
+ * @param {string} kind - Component kind
29
+ * @returns {string|null} null when absent
30
+ */
31
+ export function declaredVisibility(element, kind) {
32
+ const text = attr(element, "visibility");
33
+ if (text === undefined) return null;
34
+ const value = text.trim();
35
+ if (!(kind === "mode" ? MODE_DECLARED : DECLARED).has(value)) {
36
+ throw xsltError("XTSE0020", `Invalid visibility ${text}`);
37
+ }
38
+ return value;
39
+ }
40
+
41
+ /**
42
+ * The visibility of a component declared in a package.
43
+ * @param {object} component - `{kind, uri, local, arity, declared, isParam}`
44
+ * @param {object[]} rules - Parsed xsl:expose rules
45
+ * @returns {string}
46
+ */
47
+ function exposedVisibility(component, rules) {
48
+ if (component.isParam) return "public";
49
+ const [best] = matchesOf(rules, component);
50
+ const { declared } = component;
51
+ let visibility = declared ?? best?.rule.visibility ?? "private";
52
+ if (best?.precision === EXPLICIT) {
53
+ visibility = best.rule.visibility;
54
+ if (declared && !EXPOSABLE[visibility].includes(declared)) {
55
+ throw xsltError(
56
+ "XTSE3010",
57
+ `xsl:expose makes the ${declared} ${component.kind} ${component.local} ${visibility}`,
58
+ );
59
+ }
60
+ }
61
+ if (visibility === "abstract" && declared !== "abstract") {
62
+ throw xsltError(
63
+ "XTSE3025",
64
+ `xsl:expose cannot make ${component.local} abstract`,
65
+ );
66
+ }
67
+ return visibility;
68
+ }
69
+
70
+ /**
71
+ * A component descriptor.
72
+ * @param {string} kind
73
+ * @param {{uri: string, local: string}} name
74
+ * @param {Element} element - Declaration
75
+ * @param {object} [extra] - `arity`, `isParam`
76
+ * @returns {object}
77
+ */
78
+ function component(kind, name, element, extra = {}) {
79
+ const key = clarkOf(name);
80
+ return {
81
+ kind,
82
+ uri: name.uri,
83
+ local: name.local,
84
+ arity: extra.arity ?? null,
85
+ isParam: extra.isParam ?? false,
86
+ key: extra.arity === undefined ? key : `${key}#${extra.arity}`,
87
+ element,
88
+ declared: extra.isParam ? null : declaredVisibility(element, kind),
89
+ };
90
+ }
91
+
92
+ /**
93
+ * The named components declared in a package (the declarations of
94
+ * highest import precedence).
95
+ * @param {(kind: string) => object[]} all - Declarations by kind
96
+ * @param {object} cx - Stylesheet compiler (after compileContext)
97
+ * @param {{signatures: Map, globals: Map}} context - From compileContext
98
+ * @returns {object[]} components `{kind, uri, local, arity, key, element,
99
+ * declared, isParam}`
100
+ */
101
+ export function declaredComponents(all, cx, { signatures, globals }) {
102
+ const qname = (element) => cx.exprs.qname(attr(element, "name"), element);
103
+ // the named templates of highest precedence (XTSE0660 is raised when
104
+ // they are compiled)
105
+ const named = (declarations) => {
106
+ const chosen = new Map();
107
+ for (const declaration of declarations) {
108
+ if (attr(declaration.element, "name") === undefined) continue;
109
+ const key = clarkOf(qname(declaration.element));
110
+ if (!(chosen.get(key)?.precedence > declaration.precedence)) {
111
+ chosen.set(key, declaration);
112
+ }
113
+ }
114
+ return chosen;
115
+ };
116
+ for (const { element } of all("template")) {
117
+ if (attr(element, "name") === undefined && attr(element, "visibility")) {
118
+ throw xsltError("XTSE0500", "A template rule has no visibility");
119
+ }
120
+ }
121
+ const list = [];
122
+ for (const { element } of named(all("template")).values()) {
123
+ list.push(component("template", qname(element), element));
124
+ }
125
+ for (const { signature } of signatures.values()) {
126
+ const { element } = signature.declaration;
127
+ list.push(
128
+ component("function", signature, element, { arity: signature.arity }),
129
+ );
130
+ }
131
+ for (const { element } of globals.values()) {
132
+ const isParam = isXsl(element, "param");
133
+ list.push(component("variable", qname(element), element, { isParam }));
134
+ }
135
+ // attribute sets and modes may be declared several times
136
+ const byName = new Map();
137
+ for (const { element } of all("attribute-set")) {
138
+ const name = cx.exprs.qname(required(element, "name"), element);
139
+ byName.set(`a${clarkOf(name)}`, component("attribute-set", name, element));
140
+ }
141
+ for (const { element } of all("mode")) {
142
+ if (attr(element, "name") === undefined) {
143
+ if (
144
+ !["private", undefined].includes(attr(element, "visibility")?.trim())
145
+ ) {
146
+ throw xsltError("XTSE0020", "The unnamed mode is always private");
147
+ }
148
+ continue;
149
+ }
150
+ const name = qname(element);
151
+ const mode = component("mode", name, element);
152
+ // several xsl:mode declarations: the one with a visibility counts
153
+ if (mode.declared || !byName.has(`m${clarkOf(name)}`)) {
154
+ byName.set(`m${clarkOf(name)}`, mode);
155
+ }
156
+ }
157
+ return [...list, ...byName.values()];
158
+ }
159
+
160
+ /**
161
+ * Gives the declared components their visibility.
162
+ * @param {object[]} components - From {@link declaredComponents}
163
+ * @param {object[]} rules - Parsed xsl:expose rules
164
+ * @param {boolean} implicit - An implicit package (xsl:stylesheet): its
165
+ * named templates and modes are public unless declared otherwise
166
+ */
167
+ export function exposeComponents(components, rules, implicit) {
168
+ for (const item of components) {
169
+ item.visibility = exposedVisibility(item, rules);
170
+ if (
171
+ implicit &&
172
+ !item.declared &&
173
+ ["template", "mode"].includes(item.kind)
174
+ ) {
175
+ item.visibility = "public";
176
+ }
177
+ }
178
+ checkUnmatched(rules, "XTSE3020");
179
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * xsl:context-item (XSLT 3.0 section 10.1.1): the context item a
3
+ * template requires (use="required", XTTE3090 when absent), accepts
4
+ * (optional) or ignores (absent: the template runs with an absent
5
+ * focus), and its type (XTTE0590 when it does not match; no conversion).
6
+ *
7
+ * @module @tradik/xslt3/xslt/compiler/contextItem
8
+ */
9
+
10
+ import { matchesSequenceType } from "../../xpath/eval/sequenceType.js";
11
+ import { derive } from "../runtime/context.js";
12
+ import { attr, isXsl, xsltError } from "../names.js";
13
+ import { checkAttributes } from "./attributes.js";
14
+ import { isWhitespaceText } from "./children.js";
15
+
16
+ /** Values of the use attribute. */
17
+ const USES = new Set(["required", "optional", "absent"]);
18
+
19
+ /**
20
+ * Compiles the type of an `as` attribute that must be an item type.
21
+ * @param {Element} element
22
+ * @param {object} cx
23
+ * @returns {object|null} a sequence type of exactly one item
24
+ */
25
+ function itemType(element, cx) {
26
+ const text = attr(element, "as");
27
+ if (text === undefined) return null;
28
+ const type = cx.exprs.sequenceType(text, element);
29
+ if (type.occurrence !== "" || type.itemType === null) {
30
+ throw xsltError("XTSE0020", `"${text}" is not an item type`);
31
+ }
32
+ return type;
33
+ }
34
+
35
+ /**
36
+ * The step that checks the context item of a template.
37
+ * @param {Element} element - xsl:context-item
38
+ * @param {boolean} named - The template has a name
39
+ * @param {object} cx
40
+ * @returns {{binding: Function}}
41
+ */
42
+ function compileCheck(element, named, cx) {
43
+ checkAttributes(element);
44
+ const use = (attr(element, "use") ?? "optional").trim();
45
+ if (!USES.has(use)) throw xsltError("XTSE0020", `Invalid use "${use}"`);
46
+ if (use === "absent" && attr(element, "as") !== undefined) {
47
+ throw xsltError("XTSE3088", 'use="absent" cannot have a type');
48
+ }
49
+ if (!named && use !== "required") {
50
+ throw xsltError("XTSE0020", "A template rule requires its context item");
51
+ }
52
+ const type = itemType(element, cx);
53
+ return {
54
+ binding(xc) {
55
+ if (use === "absent") {
56
+ // no focus, hence no current template rule either (XTDE0560)
57
+ return derive(xc, {
58
+ item: undefined,
59
+ position: 0,
60
+ size: 0,
61
+ rule: null,
62
+ });
63
+ }
64
+ if (xc.item === undefined) {
65
+ if (use === "required") {
66
+ throw xsltError("XTTE3090", "The template requires a context item");
67
+ }
68
+ } else if (type && !matchesSequenceType([xc.item], type)) {
69
+ throw xsltError("XTTE0590", "The context item has the wrong type");
70
+ }
71
+ return xc;
72
+ },
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Separates the xsl:context-item of a template from its other children.
78
+ * @param {Array<object>} children - Significant children of the template
79
+ * @param {Element} template
80
+ * @param {object} cx
81
+ * @returns {{rest: Array<object>, steps: Array}} the other children, and
82
+ * the step that checks the context item (to run first)
83
+ */
84
+ export function splitContextItem(children, template, cx) {
85
+ const index = children.findIndex((child) => !isWhitespaceText(child));
86
+ const first = children[index];
87
+ const declared = first !== undefined && isXsl(first, "context-item");
88
+ const rest = declared ? children.slice(index + 1) : children;
89
+ if (rest.some((child) => isXsl(child, "context-item"))) {
90
+ throw xsltError("XTSE0010", "xsl:context-item must be the first child");
91
+ }
92
+ if (!declared) return { rest, steps: [] };
93
+ const named = attr(template, "name") !== undefined;
94
+ return { rest, steps: [compileCheck(first, named, cx)] };
95
+ }