@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,160 @@
1
+ /**
2
+ * xsl:expose and xsl:accept (XSLT 3.0 sections 3.5.3.1 and 3.5.3.2): the
3
+ * rules that give components their visibility, chosen by how precisely
4
+ * a token of `names` matches a component (an explicit name beats a
5
+ * namespace or local-name wildcard, which beats `*`; the last such rule
6
+ * in document order wins).
7
+ *
8
+ * @module @tradik/xslt3/xslt/compiler/visibility
9
+ */
10
+
11
+ import { attr, resolveQName, tokens, xsltError } from "../names.js";
12
+ import { required } from "./attributes.js";
13
+ import { infoOf } from "./elementInfo.js";
14
+
15
+ /** Component kinds that xsl:expose and xsl:accept name. */
16
+ const KINDS = new Set([
17
+ "template",
18
+ "function",
19
+ "attribute-set",
20
+ "variable",
21
+ "mode",
22
+ ]);
23
+
24
+ /** How precisely a token matches: higher wins. */
25
+ export const EXPLICIT = 3;
26
+ const PARTIAL = 2;
27
+ const ANY = 1;
28
+
29
+ /**
30
+ * Parses a token of a names attribute.
31
+ * @param {string} token
32
+ * @param {Element} element
33
+ * @returns {object} `{precision, uri, local, arity}` (null for wildcards)
34
+ */
35
+ function parseToken(token, element) {
36
+ const namespaces = infoOf(element).namespaces;
37
+ if (token === "*") return { precision: ANY };
38
+ if (token.startsWith("*:")) {
39
+ return { precision: PARTIAL, uri: null, local: token.slice(2) };
40
+ }
41
+ const eqWildcard = /^Q\{([^{}]*)\}\*$/.exec(token);
42
+ if (eqWildcard) {
43
+ return { precision: PARTIAL, uri: eqWildcard[1], local: null };
44
+ }
45
+ if (token.endsWith(":*")) {
46
+ const { uri } = resolveQName(`${token.slice(0, -2)}:x`, namespaces, {
47
+ code: "XTSE0020",
48
+ });
49
+ return { precision: PARTIAL, uri, local: null };
50
+ }
51
+ const hash = token.lastIndexOf("#");
52
+ const arity = hash < 0 ? null : token.slice(hash + 1);
53
+ if (arity !== null && !/^\d+$/.test(arity)) {
54
+ throw xsltError("XTSE0020", `Invalid name ${token}`);
55
+ }
56
+ const name = resolveQName(
57
+ hash < 0 ? token : token.slice(0, hash),
58
+ namespaces,
59
+ { code: "XTSE0020" },
60
+ );
61
+ return {
62
+ precision: EXPLICIT,
63
+ uri: name.uri,
64
+ local: name.local,
65
+ arity: arity === null ? null : Number(arity),
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Parses xsl:expose or xsl:accept elements.
71
+ * @param {Element[]} elements - In document order
72
+ * @param {object} cx - Stylesheet compiler
73
+ * @param {object} options
74
+ * @param {Set<string>} options.allowed - Allowed visibility values
75
+ * @param {string} options.anyCode - Error of component="*" with a
76
+ * non-wildcard name (XTSE3022, XTSE3032)
77
+ * @returns {object[]} rules `{kind, visibility, tokens, element}`
78
+ */
79
+ export function parseVisibilityRules(elements, cx, { allowed, anyCode }) {
80
+ return elements.map((element) => {
81
+ if (cx.children(element).length > 0) {
82
+ throw xsltError("XTSE0260", `xsl:${element.localName} must be empty`);
83
+ }
84
+ const kind = required(element, "component").trim();
85
+ if (kind !== "*" && !KINDS.has(kind)) {
86
+ throw xsltError("XTSE0020", `Invalid component ${kind}`);
87
+ }
88
+ const visibility = required(element, "visibility").trim();
89
+ if (!allowed.has(visibility)) {
90
+ throw xsltError("XTSE0020", `Invalid visibility ${visibility}`);
91
+ }
92
+ const parsed = tokens(required(element, "names")).map((token) =>
93
+ parseToken(token, element),
94
+ );
95
+ if (kind === "*" && parsed.some((t) => t.precision === EXPLICIT)) {
96
+ throw xsltError(anyCode, 'component="*" needs wildcard names');
97
+ }
98
+ return { kind, visibility, tokens: parsed, element };
99
+ });
100
+ }
101
+
102
+ /**
103
+ * How precisely a token matches a component.
104
+ * @param {object} token
105
+ * @param {object} component - `{kind, uri, local, arity}`
106
+ * @returns {number} 0 when it does not match
107
+ */
108
+ function tokenMatch(token, component) {
109
+ if (token.precision === ANY) return ANY;
110
+ if (token.precision === PARTIAL) {
111
+ const matches =
112
+ token.uri === null
113
+ ? token.local === component.local
114
+ : token.uri === component.uri;
115
+ return matches ? PARTIAL : 0;
116
+ }
117
+ if (token.uri !== component.uri || token.local !== component.local) return 0;
118
+ if (token.arity !== null && component.kind !== "function") return 0;
119
+ // a function is named with its arity
120
+ if (component.kind === "function") {
121
+ return token.arity === component.arity ? EXPLICIT : 0;
122
+ }
123
+ return EXPLICIT;
124
+ }
125
+
126
+ /**
127
+ * The matches of the rules for a component, best first.
128
+ * @param {object[]} rules
129
+ * @param {object} component
130
+ * @returns {Array<{rule: object, token: object, precision: number}>}
131
+ */
132
+ export function matchesOf(rules, component) {
133
+ const found = [];
134
+ rules.forEach((rule, order) => {
135
+ if (rule.kind !== "*" && rule.kind !== component.kind) return;
136
+ for (const token of rule.tokens) {
137
+ const precision = tokenMatch(token, component);
138
+ if (precision > 0) {
139
+ token.used = true;
140
+ found.push({ rule, token, precision, order });
141
+ }
142
+ }
143
+ });
144
+ return found.sort((a, b) => b.precision - a.precision || b.order - a.order);
145
+ }
146
+
147
+ /**
148
+ * Checks that every explicit token matched a component.
149
+ * @param {object[]} rules
150
+ * @param {string} code - XTSE3020 (expose) or XTSE3030 (accept)
151
+ */
152
+ export function checkUnmatched(rules, code) {
153
+ for (const rule of rules) {
154
+ for (const token of rule.tokens) {
155
+ if (token.precision === EXPLICIT && !token.used) {
156
+ throw xsltError(code, `${attr(rule.element, "names")} matches nothing`);
157
+ }
158
+ }
159
+ }
160
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * XSLT 3.0 and 2.0 (and 1.0 in backwards-compatible mode): the public
3
+ * API of the transformation engine.
4
+ *
5
+ * @module @tradik/xslt3/xslt
6
+ */
7
+
8
+ export { CompiledStylesheet, compileStylesheet } from "./api.js";
9
+ export { XSLTProcessor } from "./processor.js";
10
+ export { versionMatches } from "./compiler/packageVersions.js";
11
+ export { untypedAtomic } from "./runtime/params.js";
@@ -0,0 +1,137 @@
1
+ /**
2
+ * xsl:analyze-string (XSLT 3.0 section 17.1): the matching and
3
+ * non-matching substrings of a string, each processed with the substring
4
+ * as context item; regex-group() gives the captured groups.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/analyzeString
7
+ */
8
+
9
+ import { allMatches, compileRegex } from "../../functions/regex/compile.js";
10
+ import { stringItem } from "../../xpath/eval/atomics.js";
11
+ import { atomize } from "../../xdm/nodes.js";
12
+ import { canonicalString } from "../../xdm/lexical.js";
13
+ import { compileBody } from "../compiler/body.js";
14
+ import { checkAttributes, required } from "../compiler/attributes.js";
15
+ import { derive, evaluate } from "../runtime/context.js";
16
+ import { BodyFrame, LoopFrame } from "../runtime/machine.js";
17
+ import { avtEvaluator } from "../runtime/values.js";
18
+ import { attr, isXsl, xsltError } from "../names.js";
19
+
20
+ /** Types accepted as the string of xsl:analyze-string. */
21
+ const STRING_LIKE = new Set(["string", "untypedAtomic", "anyURI"]);
22
+
23
+ /**
24
+ * Compiles the regular expression, mapping its errors to XSLT codes.
25
+ * @param {string} regex
26
+ * @param {string} flags
27
+ * @returns {object}
28
+ */
29
+ function regexOf(regex, flags) {
30
+ let compiled;
31
+ try {
32
+ compiled = compileRegex(regex, flags);
33
+ } catch (error) {
34
+ const code = error.code === "FORX0001" ? "XTDE1145" : "XTDE1140";
35
+ throw xsltError(code, error.message);
36
+ }
37
+ compiled.regex.lastIndex = 0;
38
+ if (compiled.regex.test("")) {
39
+ throw xsltError("XTDE1150", 'The regular expression matches ""');
40
+ }
41
+ return compiled;
42
+ }
43
+
44
+ /**
45
+ * Splits a string into matching and non-matching substrings.
46
+ * @param {string} input
47
+ * @param {object} compiled
48
+ * @returns {Array<{text: string, groups: string[]|null}>}
49
+ */
50
+ function segmentsOf(input, compiled) {
51
+ const segments = [];
52
+ let last = 0;
53
+ for (const match of allMatches(compiled, input)) {
54
+ if (match.index > last) {
55
+ segments.push({ text: input.slice(last, match.index), groups: null });
56
+ }
57
+ const groups = [];
58
+ for (let i = 0; i <= compiled.groups; i++) groups.push(match[i] ?? "");
59
+ segments.push({ text: match[0], groups });
60
+ last = match.index + match[0].length;
61
+ }
62
+ if (last < input.length) {
63
+ segments.push({ text: input.slice(last), groups: null });
64
+ }
65
+ return segments;
66
+ }
67
+
68
+ /**
69
+ * xsl:analyze-string.
70
+ * @param {Element} element
71
+ * @param {object} cx
72
+ * @param {object} scope
73
+ * @returns {Function}
74
+ */
75
+ export function compileAnalyzeString(element, cx, scope) {
76
+ const select = cx.exprs.xpath(
77
+ required(element, "select"),
78
+ element,
79
+ scope.vars,
80
+ );
81
+ const regex = avtEvaluator(
82
+ cx.exprs.avt(required(element, "regex"), element, scope.vars),
83
+ );
84
+ const flagsText = attr(element, "flags");
85
+ const flags = flagsText
86
+ ? avtEvaluator(cx.exprs.avt(flagsText, element, scope.vars))
87
+ : () => "";
88
+ let matching = null;
89
+ let nonMatching = null;
90
+ let fallback = false;
91
+ for (const child of cx.children(element)) {
92
+ if (fallback && !isXsl(child, "fallback")) {
93
+ throw xsltError("XTSE0010", "xsl:fallback must come last");
94
+ }
95
+ if (isXsl(child, "matching-substring") && !matching && !nonMatching) {
96
+ checkAttributes(child);
97
+ matching = compileBody(child, cx, scope);
98
+ } else if (isXsl(child, "non-matching-substring") && !nonMatching) {
99
+ checkAttributes(child);
100
+ nonMatching = compileBody(child, cx, scope);
101
+ } else if (isXsl(child, "fallback")) fallback = true;
102
+ else throw xsltError("XTSE0010", "Invalid content of xsl:analyze-string");
103
+ }
104
+ if (!matching && !nonMatching) {
105
+ throw xsltError("XTSE1130", "xsl:analyze-string needs a substring child");
106
+ }
107
+ return (xc, out, machine) => {
108
+ const values = atomize(evaluate(select, xc));
109
+ if (values.length > 1) {
110
+ throw xsltError("XPTY0004", "xsl:analyze-string selects one string");
111
+ }
112
+ const primitive = values[0]?.type.primitive.localName;
113
+ if (values.length && !STRING_LIKE.has(primitive)) {
114
+ throw xsltError("XPTY0004", "xsl:analyze-string selects a string");
115
+ }
116
+ const input = values.length ? canonicalString(values[0]) : "";
117
+ const compiled = regexOf(regex(xc), flags(xc));
118
+ const segments = segmentsOf(input, compiled);
119
+ const size = segments.length;
120
+ if (size === 0) return;
121
+ const base = derive(xc, { rule: null });
122
+ machine.push(
123
+ new LoopFrame(size, (i) => {
124
+ const { text, groups } = segments[i];
125
+ const body = groups ? matching : nonMatching;
126
+ if (!body || body.length === 0) return;
127
+ const context = derive(base, {
128
+ item: stringItem(text),
129
+ position: i + 1,
130
+ size,
131
+ regex: groups ?? undefined,
132
+ });
133
+ machine.push(new BodyFrame(body, context, out));
134
+ }),
135
+ );
136
+ };
137
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * xsl:assert (XSLT 3.0 section 23.2): when its test is false, or fails,
3
+ * the transformation fails as with xsl:message terminate="yes", with the
4
+ * error code of the error-code attribute (default XTMM9001). Assertions
5
+ * are checked unless the transform() option `assertions` is false.
6
+ *
7
+ * @module @tradik/xslt3/xslt/instructions/assert
8
+ */
9
+
10
+ import { effectiveBooleanValue } from "../../xdm/nodes.js";
11
+ import { required } from "../compiler/attributes.js";
12
+ import { compileBody } from "../compiler/body.js";
13
+ import { infoOf } from "../compiler/elementInfo.js";
14
+ import { evaluate } from "../runtime/context.js";
15
+ import { avtEvaluator, bodySequence } from "../runtime/values.js";
16
+ import { attr, isXsl, resolveQName, xsltError } from "../names.js";
17
+
18
+ /** Namespace of the standard error codes. */
19
+ const ERR = "http://www.w3.org/2005/xqt-errors";
20
+
21
+ /**
22
+ * Whether the test of an assertion holds; an error counts as false.
23
+ * @param {object} test
24
+ * @param {object} xc
25
+ * @returns {boolean}
26
+ */
27
+ function holds(test, xc) {
28
+ try {
29
+ return effectiveBooleanValue(evaluate(test, xc));
30
+ } catch (error) {
31
+ if (typeof error?.code !== "string") throw error;
32
+ return false;
33
+ }
34
+ }
35
+
36
+ /**
37
+ * xsl:assert.
38
+ * @param {Element} element
39
+ * @param {object} cx
40
+ * @param {object} scope
41
+ * @returns {Function}
42
+ */
43
+ export function compileAssert(element, cx, scope) {
44
+ const test = cx.exprs.xpath(required(element, "test"), element, scope.vars);
45
+ const selectText = attr(element, "select");
46
+ const select =
47
+ selectText === undefined
48
+ ? null
49
+ : cx.exprs.xpath(selectText, element, scope.vars);
50
+ const children = cx.children(element).filter((c) => !isXsl(c, "fallback"));
51
+ if (select && children.length > 0) {
52
+ throw xsltError("XTSE3185", "xsl:assert cannot have select and content");
53
+ }
54
+ const body = compileBody(element, cx, scope, children);
55
+ const codeText = attr(element, "error-code");
56
+ const errorCode = codeText
57
+ ? avtEvaluator(cx.exprs.avt(codeText, element, scope.vars))
58
+ : () => `Q{${ERR}}XTMM9001`;
59
+ const namespaces = infoOf(element).namespaces;
60
+ return (xc, out, machine) => {
61
+ if (!xc.tx.assertions || holds(test, xc)) return;
62
+ const value = select
63
+ ? evaluate(select, xc)
64
+ : bodySequence(body, xc, machine);
65
+ xc.tx.messages.push(value);
66
+ const name = resolveQName(errorCode(xc), namespaces, { code: "XTDE0030" });
67
+ const code = name.uri === ERR ? name.local : `Q{${name.uri}}${name.local}`;
68
+ const error = xsltError(code, "Assertion failed");
69
+ error.value = value;
70
+ throw error;
71
+ };
72
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Attribute sets (XSLT 3.0 section 10.2): named groups of xsl:attribute
3
+ * instructions, applied by use-attribute-sets on literal result elements,
4
+ * xsl:element, xsl:copy and other attribute sets.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/attributeSets
7
+ */
8
+
9
+ import { compileBody } from "../compiler/body.js";
10
+ import { required } from "../compiler/attributes.js";
11
+ import { derive } from "../runtime/context.js";
12
+ import { clarkOf, isXsl, tokens, xsltError } from "../names.js";
13
+
14
+ /**
15
+ * Resolves the names of a use-attribute-sets attribute; their existence
16
+ * is checked once the stylesheet is compiled (XTSE0710).
17
+ * @param {string|undefined} text
18
+ * @param {Element} element
19
+ * @param {object} cx - Stylesheet compiler
20
+ * @returns {string[]} Clark names
21
+ */
22
+ export function attributeSetNames(text, element, cx) {
23
+ const names = tokens(text).map((token) => {
24
+ const name = clarkOf(cx.exprs.qname(token, element, { code: "XTSE0710" }));
25
+ return cx.originalName("attribute-set", name) ?? name;
26
+ });
27
+ for (const name of names) {
28
+ cx.deferred.push(() => {
29
+ if (!cx.attributeSets.has(name)) {
30
+ throw xsltError("XTSE0710", `No attribute set ${name}`);
31
+ }
32
+ });
33
+ }
34
+ return names;
35
+ }
36
+
37
+ /**
38
+ * Compiles an xsl:attribute-set declaration into the registry.
39
+ * @param {Element} element
40
+ * @param {object} cx
41
+ */
42
+ export function declareAttributeSet(element, cx) {
43
+ const name = clarkOf(cx.exprs.qname(required(element, "name"), element));
44
+ const children = cx.children(element);
45
+ for (const child of children) {
46
+ if (child.nodeType !== 1 || !isXsl(child, "attribute")) {
47
+ throw xsltError(
48
+ "XTSE0010",
49
+ "An attribute set may only contain xsl:attribute",
50
+ );
51
+ }
52
+ }
53
+ const part = {
54
+ uses: attributeSetNames(
55
+ element.getAttribute("use-attribute-sets") || undefined,
56
+ element,
57
+ cx,
58
+ ),
59
+ body: compileBody(element, cx, cx.globalScope()),
60
+ precedence: cx.precedenceOf(element),
61
+ cx,
62
+ };
63
+ const parts = cx.attributeSets.get(name) ?? [];
64
+ parts.push(part);
65
+ parts.sort((a, b) => a.precedence - b.precedence);
66
+ cx.attributeSets.set(name, parts);
67
+ }
68
+
69
+ /**
70
+ * Adds the attributes of attribute sets to an element under construction.
71
+ * @param {string[]} names - Clark names
72
+ * @param {object} xc - XSLT context
73
+ * @param {object} out - Receiver of the element's content
74
+ * @param {import("../runtime/machine.js").Machine} machine
75
+ * @param {object} cx - Stylesheet compiler
76
+ * @param {Set<string>} [active] - Sets being applied (cycle detection)
77
+ * @param {boolean} [crossed] - The chain went through another package
78
+ */
79
+ export function applyAttributeSets(
80
+ names,
81
+ xc,
82
+ out,
83
+ machine,
84
+ cx,
85
+ active,
86
+ crossed = false,
87
+ ) {
88
+ const applying = active ?? new Set();
89
+ const context = derive(xc, { env: xc.tx.globalEnv });
90
+ for (const name of names) {
91
+ if (applying.has(name)) {
92
+ // a cycle made by overriding in another package shows only when
93
+ // the components are bound: a dynamic error (XSLT 3.0 10.2)
94
+ throw xsltError(
95
+ crossed ? "XTDE0640" : "XTSE0720",
96
+ `The attribute set ${name} uses itself`,
97
+ );
98
+ }
99
+ applying.add(name);
100
+ for (const part of cx.attributeSets.get(name)) {
101
+ const across = crossed || part.cx !== cx;
102
+ applyAttributeSets(
103
+ part.uses,
104
+ xc,
105
+ out,
106
+ machine,
107
+ part.cx,
108
+ applying,
109
+ across,
110
+ );
111
+ machine.runBody(part.body, context, out);
112
+ }
113
+ applying.delete(name);
114
+ }
115
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * xsl:on-empty, xsl:on-non-empty and xsl:where-populated (XSLT 3.0
3
+ * section 8.4). The first two mark their step (`conditional`); the
4
+ * sequence constructor that holds them is run by
5
+ * runtime/conditional.js (see compileBody).
6
+ *
7
+ * @module @tradik/xslt3/xslt/instructions/conditional
8
+ */
9
+
10
+ import { compileBody } from "../compiler/body.js";
11
+ import { isWhitespaceText } from "../compiler/children.js";
12
+ import { evaluate } from "../runtime/context.js";
13
+ import { isDeemedEmpty, stepItems } from "../runtime/conditional.js";
14
+ import { attr, isXsl, xsltError } from "../names.js";
15
+
16
+ /**
17
+ * Compiles the content of xsl:on-empty or xsl:on-non-empty, which is
18
+ * that of xsl:sequence.
19
+ * @param {Element} element
20
+ * @param {object} cx
21
+ * @param {object} scope
22
+ * @returns {Function}
23
+ */
24
+ function compileContent(element, cx, scope) {
25
+ const select = attr(element, "select");
26
+ const children = cx.children(element).filter((c) => !isXsl(c, "fallback"));
27
+ if (select === undefined) {
28
+ const body = compileBody(element, cx, scope, children);
29
+ return (xc, out, machine) => machine.runBody(body, xc, out);
30
+ }
31
+ if (children.length > 0) {
32
+ throw xsltError(
33
+ "XTSE3185",
34
+ `xsl:${element.localName} cannot have select and content`,
35
+ );
36
+ }
37
+ const expr = cx.exprs.xpath(select, element, scope.vars);
38
+ return (xc, out) => {
39
+ for (const item of evaluate(expr, xc)) out.item(item);
40
+ };
41
+ }
42
+
43
+ /**
44
+ * xsl:on-empty: the last instruction of its sequence constructor.
45
+ * @param {Element} element
46
+ * @param {object} cx
47
+ * @param {object} scope
48
+ * @returns {Function}
49
+ */
50
+ export function compileOnEmpty(element, cx, scope) {
51
+ const siblings = cx.children(element.parentNode);
52
+ const after = siblings.slice(siblings.indexOf(element) + 1);
53
+ const misplaced = after.some(
54
+ (next) =>
55
+ !isWhitespaceText(next) &&
56
+ !isXsl(next, "fallback") &&
57
+ !isXsl(next, "catch"),
58
+ );
59
+ if (misplaced) {
60
+ throw xsltError(
61
+ "XTSE0010",
62
+ "xsl:on-empty must be the last instruction of a sequence constructor",
63
+ );
64
+ }
65
+ const step = compileContent(element, cx, scope);
66
+ step.conditional = "empty";
67
+ return step;
68
+ }
69
+
70
+ /**
71
+ * xsl:on-non-empty.
72
+ * @param {Element} element
73
+ * @param {object} cx
74
+ * @param {object} scope
75
+ * @returns {Function}
76
+ */
77
+ export function compileOnNonEmpty(element, cx, scope) {
78
+ const step = compileContent(element, cx, scope);
79
+ step.conditional = "non-empty";
80
+ return step;
81
+ }
82
+
83
+ /**
84
+ * xsl:where-populated: the result of the content without the items
85
+ * deemed empty.
86
+ * @param {Element} element
87
+ * @param {object} cx
88
+ * @param {object} scope
89
+ * @returns {Function}
90
+ */
91
+ export function compileWherePopulated(element, cx, scope) {
92
+ const body = compileBody(element, cx, scope);
93
+ const run = (xc, out, machine) => machine.runBody(body, xc, out);
94
+ return (xc, out, machine) => {
95
+ for (const item of stepItems(run, xc, machine)) {
96
+ if (!isDeemedEmpty(item)) out.item(item);
97
+ }
98
+ };
99
+ }