@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,186 @@
1
+ /**
2
+ * xsl:try and xsl:catch (XSLT 3.0 section 8.3): the result of the try
3
+ * body is built apart, and replaced by the result of the first matching
4
+ * xsl:catch when a dynamic error occurs; the catch body sees the error in
5
+ * the variables err:code, err:description, err:value...
6
+ *
7
+ * @module @tradik/xslt3/xslt/instructions/try
8
+ */
9
+
10
+ import { AtomicValue } from "../../xdm/atomic.js";
11
+ import { QNameValue } from "../../xdm/qname.js";
12
+ import { types } from "../../xdm/types.js";
13
+ import { compileBody } from "../compiler/body.js";
14
+ import { checkAttributes } from "../compiler/attributes.js";
15
+ import { infoOf } from "../compiler/elementInfo.js";
16
+ import { derive, evaluate } from "../runtime/context.js";
17
+ import { escapesTry, markOutside } from "../runtime/lazy.js";
18
+ import { SequenceReceiver } from "../runtime/sequenceReceiver.js";
19
+ import { attr, isXsl, tokens, xsltError } from "../names.js";
20
+
21
+ /** Namespace of the error codes and of the err:* variables. */
22
+ const ERR = "http://www.w3.org/2005/xqt-errors";
23
+
24
+ /** The variables of a catch body, outermost first. */
25
+ const ERROR_VARIABLES = [
26
+ "code",
27
+ "description",
28
+ "value",
29
+ "module",
30
+ "line-number",
31
+ "column-number",
32
+ "additional",
33
+ ];
34
+
35
+ /**
36
+ * The expanded name of an error code (`LOCAL` in the err namespace, or
37
+ * `Q{uri}local`).
38
+ * @param {string} code
39
+ * @returns {{uri: string, local: string}}
40
+ */
41
+ function codeName(code) {
42
+ const eq = /^Q\{([^}]*)\}(.*)$/.exec(code);
43
+ return eq ? { uri: eq[1], local: eq[2] } : { uri: ERR, local: code };
44
+ }
45
+
46
+ /**
47
+ * Compiles the name tests of an errors attribute.
48
+ * @param {Element} element - xsl:catch
49
+ * @returns {(name: {uri: string, local: string}) => boolean}
50
+ */
51
+ function errorTests(element) {
52
+ const namespaces = infoOf(element).namespaces;
53
+ const tests = tokens(attr(element, "errors") ?? "*").map((token) => {
54
+ if (token === "*") return () => true;
55
+ if (token.startsWith("*:")) return (n) => n.local === token.slice(2);
56
+ const eq = /^Q\{([^}]*)\}(.*)$/.exec(token);
57
+ const [uri, local] = eq
58
+ ? [eq[1], eq[2]]
59
+ : token.includes(":")
60
+ ? [namespaces.get(token.split(":")[0]) ?? "", token.split(":")[1]]
61
+ : ["", token];
62
+ return (n) => n.uri === uri && (local === "*" || n.local === local);
63
+ });
64
+ return (name) => tests.some((test) => test(name));
65
+ }
66
+
67
+ /**
68
+ * An optional atomic value.
69
+ * @param {object} type
70
+ * @param {string|number|undefined} value - Absent when undefined
71
+ * @returns {Array}
72
+ */
73
+ const optional = (type, value) =>
74
+ value === undefined
75
+ ? []
76
+ : [new AtomicValue(type, type === types.integer ? BigInt(value) : value)];
77
+
78
+ /**
79
+ * The values of the err:* variables for an error.
80
+ * @param {Error} error
81
+ * @returns {Array[]} in the order of ERROR_VARIABLES
82
+ */
83
+ function errorValues(error) {
84
+ const { uri, local } = codeName(error.code);
85
+ const description = (error.plainMessage ?? error.message).replace(
86
+ /^[^ ]+: /,
87
+ "",
88
+ );
89
+ return [
90
+ [new AtomicValue(types.QName, new QNameValue(uri, local, "err"))],
91
+ [new AtomicValue(types.string, description)],
92
+ error.errorObject ?? error.value ?? [],
93
+ // where the error was raised (see runtime/locations.js)
94
+ optional(types.string, error.location?.module),
95
+ optional(types.integer, error.location?.line),
96
+ optional(types.integer, error.location?.column),
97
+ [],
98
+ ].map((value) => (Array.isArray(value) ? value : [value]));
99
+ }
100
+
101
+ /**
102
+ * Compiles the body or select of xsl:try / xsl:catch.
103
+ * @returns {(xc: object, machine: object) => Array}
104
+ */
105
+ function compileContent(element, children, cx, scope, conflictCode) {
106
+ const select = attr(element, "select");
107
+ if (select !== undefined && children.length > 0) {
108
+ throw xsltError(
109
+ conflictCode,
110
+ `xsl:${element.localName} has select and content`,
111
+ );
112
+ }
113
+ if (select !== undefined) {
114
+ const expr = cx.exprs.xpath(select, element, scope.vars);
115
+ return (xc) => evaluate(expr, xc);
116
+ }
117
+ const body = compileBody(element, cx, scope, children);
118
+ return (xc, machine) => {
119
+ const out = new SequenceReceiver(xc.tx.scratch);
120
+ machine.runBody(body, xc, out);
121
+ return out.items;
122
+ };
123
+ }
124
+
125
+ /**
126
+ * xsl:try.
127
+ * @param {Element} element
128
+ * @param {object} cx
129
+ * @param {object} scope
130
+ * @returns {Function}
131
+ */
132
+ export function compileTry(element, cx, scope) {
133
+ const children = cx.children(element).filter((c) => !isXsl(c, "fallback"));
134
+ const first = children.findIndex((child) => isXsl(child, "catch"));
135
+ if (first < 0) throw xsltError("XTSE0010", "xsl:try needs an xsl:catch");
136
+ const catchElements = children.slice(first);
137
+ if (!catchElements.every((child) => isXsl(child, "catch"))) {
138
+ throw xsltError("XTSE0010", "xsl:catch must come last in xsl:try");
139
+ }
140
+ const content = compileContent(
141
+ element,
142
+ children.slice(0, first),
143
+ cx,
144
+ scope,
145
+ "XTSE3140",
146
+ );
147
+ let catchScope = scope;
148
+ for (const name of ERROR_VARIABLES) {
149
+ catchScope = {
150
+ ...catchScope,
151
+ vars: { key: `{${ERR}}${name}`, next: catchScope.vars },
152
+ };
153
+ }
154
+ const catches = catchElements.map((catchElement) => {
155
+ checkAttributes(catchElement);
156
+ return {
157
+ matches: errorTests(catchElement),
158
+ content: compileContent(
159
+ catchElement,
160
+ cx.children(catchElement),
161
+ cx,
162
+ catchScope,
163
+ "XTSE3150",
164
+ ),
165
+ };
166
+ });
167
+ return (xc, out, machine) => {
168
+ let items;
169
+ const marker = {};
170
+ markOutside(xc.env, xc.tx.globalEnv, marker);
171
+ try {
172
+ items = content(xc, machine);
173
+ } catch (error) {
174
+ // errors of variables declared outside are not caught (XSLT 3.0 8.3)
175
+ if (typeof error?.code !== "string" || escapesTry(error, marker)) {
176
+ throw error;
177
+ }
178
+ const handler = catches.find((c) => c.matches(codeName(error.code)));
179
+ if (!handler) throw error;
180
+ let env = xc.env;
181
+ for (const value of errorValues(error)) env = { value, next: env };
182
+ items = handler.content(derive(xc, { env }), machine);
183
+ }
184
+ for (const item of items) out.item(item);
185
+ };
186
+ }
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Values of variables and parameters (XSLT 3.0 section 9.3): the select
3
+ * expression, or the content as a temporary tree (as a sequence when
4
+ * there is an `as` attribute), converted to the declared type.
5
+ *
6
+ * @module @tradik/xslt3/xslt/instructions/variables
7
+ */
8
+
9
+ import { setBaseUri } from "../../functions/baseUris.js";
10
+ import { stringItem } from "../../xpath/eval/atomics.js";
11
+ import { coerce } from "../../xpath/eval/coercion.js";
12
+ import { compileBody } from "../compiler/body.js";
13
+ import { required } from "../compiler/attributes.js";
14
+ import { infoOf } from "../compiler/elementInfo.js";
15
+ import { derive, evaluate } from "../runtime/context.js";
16
+ import { LazyEntry } from "../runtime/lazy.js";
17
+ import { bodySequence, temporaryTree } from "../runtime/values.js";
18
+ import { attr, clarkOf, declaredName, isXsl, xsltError } from "../names.js";
19
+
20
+ /**
21
+ * A converter to a declared sequence type, raising a given error code
22
+ * when the value does not match.
23
+ * @param {object|null} type - Compiled sequence type, null for none
24
+ * @param {string} code - Error code for a type mismatch
25
+ * @param {string} what - Description for messages
26
+ * @param {boolean} [compatible] - XPath 1.0 compatibility
27
+ * @returns {(value: Array) => Array}
28
+ */
29
+ export function typeConverter(type, code, what, compatible = false) {
30
+ if (!type) return (value) => value;
31
+ return (value) => {
32
+ try {
33
+ return coerce(value, type, { what, compatible });
34
+ } catch (error) {
35
+ if (error.code === "XPTY0004" || error.code === "FORG0001") {
36
+ throw xsltError(code, error.message.replace(/^[A-Z]{4}\d{4}: /, ""));
37
+ }
38
+ throw error;
39
+ }
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Compiles the value of a variable, parameter or with-param.
45
+ * @param {Element} element
46
+ * @param {object} cx
47
+ * @param {object} scope - Scope of the select expression and content
48
+ * @param {string} code - Error code of a type mismatch
49
+ * @returns {{value: (xc: object, machine: object) => Array, type: object|null,
50
+ * hasDefault: boolean}}
51
+ */
52
+ export function compileValue(element, cx, scope, code) {
53
+ const select = attr(element, "select");
54
+ const asText = attr(element, "as");
55
+ const type =
56
+ asText === undefined ? null : cx.exprs.sequenceType(asText, element);
57
+ const children = cx
58
+ .children(element)
59
+ .filter((child) => !isXsl(child, "fallback"));
60
+ if (select !== undefined && children.length > 0) {
61
+ throw xsltError(
62
+ "XTSE0620",
63
+ `xsl:${element.localName} cannot have both select and content`,
64
+ );
65
+ }
66
+ const name = attr(element, "name") ?? "";
67
+ const convert = typeConverter(
68
+ type,
69
+ code,
70
+ `value of $${name}`,
71
+ infoOf(element).version < 2,
72
+ );
73
+ const hasDefault = select !== undefined || children.length > 0;
74
+ if (select !== undefined) {
75
+ const expr = cx.exprs.xpath(select, element, scope.vars);
76
+ return { value: (xc) => convert(evaluate(expr, xc)), type, hasDefault };
77
+ }
78
+ if (children.length > 0) {
79
+ const body = compileBody(element, cx, scope, children);
80
+ const base = infoOf(element).baseUri;
81
+ const value = type
82
+ ? (xc, machine) => convert(bodySequence(body, xc, machine))
83
+ : (xc, machine) => [setBaseUri(temporaryTree(body, xc, machine), base)];
84
+ return { value, type, hasDefault };
85
+ }
86
+ const empty = type ? [] : [stringItem("")];
87
+ return { value: () => convert(empty), type, hasDefault };
88
+ }
89
+
90
+ /**
91
+ * The Clark name of a variable or parameter.
92
+ * @param {Element} element
93
+ * @param {object} cx
94
+ * @returns {string}
95
+ */
96
+ export const variableName = (element, cx) =>
97
+ clarkOf(declaredName(cx.exprs.qname(required(element, "name"), element)));
98
+
99
+ /**
100
+ * Compiles a local xsl:variable.
101
+ * @param {Element} element
102
+ * @param {object} cx
103
+ * @param {object} scope
104
+ * @returns {{step: {binding: Function}, scope: object}} the binding step and
105
+ * the scope of the following siblings
106
+ */
107
+ export function compileLocalVariable(element, cx, scope) {
108
+ const key = variableName(element, cx);
109
+ const { value } = compileValue(element, cx, scope, "XTTE0570");
110
+ return {
111
+ step: {
112
+ binding: (xc, machine) =>
113
+ derive(xc, {
114
+ env: new LazyEntry(xc.env, () => value(xc, machine), key),
115
+ }),
116
+ },
117
+ scope: { ...scope, vars: { key, next: scope.vars } },
118
+ };
119
+ }
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Names shared by the XSLT compiler and runtime: namespace URIs, the
3
+ * errors of the XSLT specification, attribute access and the resolution
4
+ * of the QNames written in stylesheet attributes.
5
+ *
6
+ * @module @tradik/xslt3/xslt/names
7
+ */
8
+
9
+ import { XPathError } from "../errors.js";
10
+
11
+ /** The XSLT namespace. */
12
+ export const XSL_NS = "http://www.w3.org/1999/XSL/Transform";
13
+ /** Namespace of namespace declarations. */
14
+ export const XMLNS_NS = "http://www.w3.org/2000/xmlns/";
15
+ /** The namespace bound to the xml prefix. */
16
+ export const XML_NS = "http://www.w3.org/XML/1998/namespace";
17
+
18
+ // combining marks (U+0300-036F) are in \p{M}
19
+ const NCNAME = /^[\p{L}_][\p{L}\p{N}\p{M}_.\-·‿⁀]*$/u;
20
+
21
+ /**
22
+ * @param {string} text
23
+ * @returns {boolean} whether the text is an NCName
24
+ */
25
+ export const isNCName = (text) => NCNAME.test(text);
26
+
27
+ /**
28
+ * @param {string} text
29
+ * @returns {boolean} whether the text is a lexical QName
30
+ */
31
+ export function isQName(text) {
32
+ const colon = text.indexOf(":");
33
+ if (colon < 0) return isNCName(text);
34
+ return isNCName(text.slice(0, colon)) && isNCName(text.slice(colon + 1));
35
+ }
36
+
37
+ /**
38
+ * An error of the XSLT specification.
39
+ * @param {string} code - Error code, e.g. "XTSE0010"
40
+ * @param {string} message
41
+ * @returns {XPathError}
42
+ */
43
+ export const xsltError = (code, message) => new XPathError(code, message);
44
+
45
+ /**
46
+ * @param {Node} node
47
+ * @param {string} [local] - Local name required
48
+ * @returns {boolean} whether the node is an element of the XSLT namespace
49
+ */
50
+ export const isXsl = (node, local) =>
51
+ node.nodeType === 1 &&
52
+ node.namespaceURI === XSL_NS &&
53
+ (local === undefined || node.localName === local);
54
+
55
+ /** @type {WeakMap<Element, Map<string, string>>} shadow attribute values */
56
+ const shadows = new WeakMap();
57
+
58
+ /**
59
+ * Records the values of the shadow attributes of an element (XSLT 3.0
60
+ * section 3.13.2): they replace the attributes of the same name.
61
+ * @param {Element} element
62
+ * @param {Map<string, string>} values - By attribute name
63
+ */
64
+ export function setShadowAttributes(element, values) {
65
+ shadows.set(element, values);
66
+ }
67
+
68
+ /**
69
+ * Value of an attribute without namespace (or of its shadow attribute).
70
+ * @param {Element} element
71
+ * @param {string} name
72
+ * @returns {string|undefined} undefined when absent
73
+ */
74
+ export function attr(element, name) {
75
+ const shadow = shadows.get(element)?.get(name);
76
+ if (shadow !== undefined) return shadow;
77
+ const node = element.getAttributeNode(name);
78
+ return node && !node.namespaceURI ? node.value : undefined;
79
+ }
80
+
81
+ /**
82
+ * Value of an attribute in the XSLT namespace (standard attributes of
83
+ * literal result elements, such as xsl:version).
84
+ * @param {Element} element
85
+ * @param {string} local
86
+ * @returns {string|undefined}
87
+ */
88
+ export function xslAttr(element, local) {
89
+ return element.hasAttributeNS(XSL_NS, local)
90
+ ? element.getAttributeNS(XSL_NS, local)
91
+ : undefined;
92
+ }
93
+
94
+ /**
95
+ * A standard attribute: unprefixed on XSLT elements, in the XSLT
96
+ * namespace on other elements.
97
+ * @param {Element} element
98
+ * @param {string} local
99
+ * @returns {string|undefined}
100
+ */
101
+ export const standardAttr = (element, local) =>
102
+ element.namespaceURI === XSL_NS
103
+ ? attr(element, local)
104
+ : xslAttr(element, local);
105
+
106
+ /**
107
+ * @typedef {object} ExpandedName
108
+ * @property {string} uri - Namespace URI, "" for none
109
+ * @property {string} local - Local part
110
+ * @property {string} prefix - Prefix as written, "" for none
111
+ */
112
+
113
+ /**
114
+ * Resolves a QName or EQName written in a stylesheet.
115
+ * @param {string} text
116
+ * @param {Map<string, string>} namespaces - In-scope namespaces
117
+ * @param {object} [options]
118
+ * @param {boolean} [options.useDefault] - Unprefixed names take the
119
+ * default namespace
120
+ * @param {string} [options.code] - Error code of an undeclared prefix
121
+ * (default XTSE0280), and of an invalid name when `syntaxCode` is absent
122
+ * @param {string} [options.syntaxCode] - Error code of an invalid name
123
+ * (default XTSE0020 with the default `code`)
124
+ * @returns {ExpandedName}
125
+ * @throws {XPathError} for an invalid name or undeclared prefix
126
+ */
127
+ export function resolveQName(text, namespaces, options = {}) {
128
+ const { useDefault = false, code = "XTSE0280" } = options;
129
+ const syntaxCode =
130
+ options.syntaxCode ?? (options.code === undefined ? "XTSE0020" : code);
131
+ const name = text.trim();
132
+ const eq = /^Q\{([^{}]*)\}(.*)$/s.exec(name);
133
+ if (eq) {
134
+ if (!isNCName(eq[2])) throw xsltError(syntaxCode, `Invalid name ${name}`);
135
+ return { uri: eq[1], local: eq[2], prefix: "" };
136
+ }
137
+ if (!isQName(name)) throw xsltError(syntaxCode, `Invalid QName "${name}"`);
138
+ const colon = name.indexOf(":");
139
+ if (colon < 0) {
140
+ const uri = useDefault ? (namespaces.get("") ?? "") : "";
141
+ return { uri, local: name, prefix: "" };
142
+ }
143
+ const prefix = name.slice(0, colon);
144
+ const uri = namespaces.get(prefix);
145
+ if (uri === undefined || uri === "") {
146
+ throw xsltError(code, `The prefix ${prefix} is not declared`);
147
+ }
148
+ return { uri, local: name.slice(colon + 1), prefix };
149
+ }
150
+
151
+ /** Namespaces in which stylesheets cannot declare names (XTSE0080). */
152
+ export const RESERVED_NAMESPACES = new Set([
153
+ XSL_NS,
154
+ "http://www.w3.org/2005/xpath-functions",
155
+ "http://www.w3.org/2005/xpath-functions/math",
156
+ "http://www.w3.org/2005/xpath-functions/map",
157
+ "http://www.w3.org/2005/xpath-functions/array",
158
+ "http://www.w3.org/2001/XMLSchema",
159
+ "http://www.w3.org/2001/XMLSchema-instance",
160
+ XML_NS,
161
+ ]);
162
+
163
+ /**
164
+ * Checks that a declared name is not in a reserved namespace.
165
+ * @param {ExpandedName} name
166
+ * @param {string} [allowed] - Clark name allowed anyway
167
+ * (`{XSL}initial-template` for templates)
168
+ * @returns {ExpandedName} the name
169
+ * @throws {XPathError} XTSE0080
170
+ */
171
+ export function declaredName(name, allowed) {
172
+ if (RESERVED_NAMESPACES.has(name.uri) && clarkOf(name) !== allowed) {
173
+ throw xsltError("XTSE0080", `The namespace ${name.uri} is reserved`);
174
+ }
175
+ return name;
176
+ }
177
+
178
+ /**
179
+ * @param {{uri: string, local: string}} name
180
+ * @returns {string} Clark notation `{uri}local`
181
+ */
182
+ export const clarkOf = (name) => `{${name.uri}}${name.local}`;
183
+
184
+ /**
185
+ * A Clark name as messages show it: `local` without namespace, else the
186
+ * EQName `Q{uri}local`.
187
+ * @param {string} clark - `{uri}local` (other names are kept)
188
+ * @returns {string}
189
+ */
190
+ export function displayName(clark) {
191
+ if (clark.startsWith("{}")) return clark.slice(2);
192
+ return clark.startsWith("{") ? `Q${clark}` : clark;
193
+ }
194
+
195
+ /**
196
+ * Splits a whitespace-separated list.
197
+ * @param {string|undefined} text
198
+ * @returns {string[]}
199
+ */
200
+ export const tokens = (text) => (text ?? "").split(/\s+/).filter(Boolean);
201
+
202
+ /** The name xsl:original, by which an overriding component reaches the
203
+ * overridden one (XSLT 3.0 section 3.5.3.4), in Clark notation. */
204
+ export const ORIGINAL = `{${XSL_NS}}original`;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * The origins of an axis step in a pattern (see general.js): the nodes
3
+ * from which the step selects a node, found through the inverse of its
4
+ * axis, with the child-or-top, attribute-or-top and namespace-or-top
5
+ * adjustments of the first step of a relative pattern (XSLT 3.0 section
6
+ * 5.5.3).
7
+ *
8
+ * @module @tradik/xslt3/xslt/patterns/axisOrigins
9
+ */
10
+
11
+ import { parentOf } from "../../xpath/eval/domNodes.js";
12
+ import { compileNode } from "../../xpath/eval/compiler.js";
13
+ import { compileNodeTest } from "../../xpath/eval/nodeTests.js";
14
+ import { compilePredicate } from "../../xpath/eval/predicates.js";
15
+
16
+ /** Node kinds the child-or-top axis selects when they have no parent. */
17
+ const TOP_KINDS = new Set([1, 3, 4, 7, 8]);
18
+
19
+ /**
20
+ * The ancestors of a node, nearest first.
21
+ * @param {Node} node
22
+ * @returns {Node[]}
23
+ */
24
+ function ancestors(node) {
25
+ const result = [];
26
+ for (let a = parentOf(node); a; a = parentOf(a)) result.push(a);
27
+ return result;
28
+ }
29
+
30
+ /**
31
+ * The nodes from which an axis can reach a node.
32
+ * @param {string} axis
33
+ * @param {Node} node
34
+ * @returns {Node[]}
35
+ */
36
+ function inverseAxis(axis, node) {
37
+ switch (axis) {
38
+ case "self":
39
+ return [node];
40
+ case "descendant":
41
+ return ancestors(node);
42
+ case "descendant-or-self":
43
+ return [node, ...ancestors(node)];
44
+ default: {
45
+ const parent = parentOf(node);
46
+ return parent ? [parent] : [];
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Whether a step selects nodes as child-or-top / attribute-or-top /
53
+ * namespace-or-top does for a parentless node (the first step of a
54
+ * relative pattern).
55
+ * @param {object} step - AxisStep
56
+ * @param {Node} node
57
+ * @returns {boolean}
58
+ */
59
+ function selectsTop(step, node) {
60
+ if (parentOf(node)) return false;
61
+ if (step.axis === "child") return TOP_KINDS.has(node.nodeType);
62
+ return step.axis === "attribute" || step.axis === "namespace";
63
+ }
64
+
65
+ /**
66
+ * Compiles predicates applied to a node alone (position 1 of 1).
67
+ * @param {object[]} predicates
68
+ * @param {object} env
69
+ * @returns {(node: Node, xc: object) => Node[]} [node] when it passes
70
+ */
71
+ export function filterAlone(predicates, env) {
72
+ const scope = { sc: env.sc, vars: env.vars };
73
+ const compiled = predicates.map((p) =>
74
+ compilePredicate(p, scope, compileNode),
75
+ );
76
+ return (node, xc) => {
77
+ const ctx = env.cx.patternContext(node, xc, env.local);
78
+ let items = [node];
79
+ for (const predicate of compiled) items = predicate(items, ctx);
80
+ return items;
81
+ };
82
+ }
83
+
84
+ /**
85
+ * Compiles an axis step: `origins(node, xc)` returns the context nodes
86
+ * from which the step selects the node.
87
+ * @param {object} step - AxisStep
88
+ * @param {object} env - See compile.js
89
+ * @param {boolean} first - First step of a relative pattern
90
+ * @param {(ctx: object) => Array} run - The step compiled as an expression
91
+ * @returns {{origins: (node: Node, xc: object) => Node[]}}
92
+ */
93
+ export function compileAxisOrigins(step, env, first, run) {
94
+ // document-node() without an axis matches document nodes (self axis)
95
+ const documentStep =
96
+ first &&
97
+ step.axis === "child" &&
98
+ step.nodeTest.type === "DocumentTest" &&
99
+ !env.text.startsWith("child::", step.start);
100
+ const axis = documentStep ? "self" : step.axis;
101
+ const effective = documentStep ? { ...step, axis } : step;
102
+ const evaluate = documentStep
103
+ ? env.cx.exprs.compileAst(effective, env.sc, env.vars)
104
+ : run;
105
+ const nodeTest = compileNodeTest(step.nodeTest, axis, env.sc);
106
+ const simple = step.predicates.length === 0;
107
+ const alone = filterAlone(step.predicates, env);
108
+ return {
109
+ origins(node, xc) {
110
+ if (first && !documentStep && selectsTop(step, node)) {
111
+ // the predicates see the node alone (position 1 of 1)
112
+ return nodeTest(node) ? alone(node, xc) : [];
113
+ }
114
+ const candidates = inverseAxis(axis, node);
115
+ if (simple) return nodeTest(node) ? candidates : [];
116
+ return candidates.filter((c) =>
117
+ evaluate(env.cx.patternContext(c, xc, env.local)).includes(node),
118
+ );
119
+ },
120
+ };
121
+ }