@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,132 @@
1
+ /**
2
+ * xsl:decimal-format declarations (XSLT 3.0 section 21.2.1): the
3
+ * properties of each format merged by import precedence and checked,
4
+ * as definitions for the XPath static context (see
5
+ * xpath/eval/decimalFormats.js).
6
+ *
7
+ * @module @tradik/xslt3/xslt/compiler/decimalFormats
8
+ */
9
+
10
+ import { clarkOf, xsltError } from "../names.js";
11
+
12
+ /** Properties holding one character. */
13
+ const CHARACTERS = [
14
+ "decimal-separator",
15
+ "grouping-separator",
16
+ "minus-sign",
17
+ "exponent-separator",
18
+ "percent",
19
+ "per-mille",
20
+ "zero-digit",
21
+ "digit",
22
+ "pattern-separator",
23
+ ];
24
+
25
+ /** Properties holding a string. */
26
+ const STRINGS = ["infinity", "NaN"];
27
+
28
+ /** Properties whose characters must differ (XTSE1300). */
29
+ const DISTINCT = [
30
+ "decimal-separator",
31
+ "grouping-separator",
32
+ "exponent-separator",
33
+ "percent",
34
+ "per-mille",
35
+ "digit",
36
+ "pattern-separator",
37
+ ];
38
+
39
+ /** Default values of the distinct properties. */
40
+ const DEFAULTS = {
41
+ "decimal-separator": ".",
42
+ "grouping-separator": ",",
43
+ "exponent-separator": "e",
44
+ percent: "%",
45
+ "per-mille": "‰",
46
+ digit: "#",
47
+ "pattern-separator": ";",
48
+ "zero-digit": "0",
49
+ };
50
+
51
+ /**
52
+ * Whether a code point is a decimal digit with value zero: Unicode
53
+ * decimal digits come in runs of ten, from zero to nine.
54
+ * @param {number} codePoint
55
+ * @returns {boolean}
56
+ */
57
+ function isZeroDigit(codePoint) {
58
+ const digit = (cp) => /^\p{Nd}$/u.test(String.fromCodePoint(cp));
59
+ if (!digit(codePoint)) return false;
60
+ let before = 0;
61
+ while (codePoint - before - 1 >= 0 && digit(codePoint - before - 1)) {
62
+ before++;
63
+ }
64
+ return before % 10 === 0;
65
+ }
66
+
67
+ /**
68
+ * Checks a merged format: zero-digit is a digit with value 0, and the
69
+ * picture characters are distinct (the ten digits included).
70
+ * @param {object} format
71
+ */
72
+ function checkFormat(format) {
73
+ const zero = format["zero-digit"] ?? "0";
74
+ const base = zero.codePointAt(0);
75
+ if (!isZeroDigit(base)) {
76
+ throw xsltError("XTSE1295", "zero-digit must be a digit with value zero");
77
+ }
78
+ const used = new Set();
79
+ for (let i = 0; i < 10; i++) used.add(String.fromCodePoint(base + i));
80
+ for (const property of DISTINCT) {
81
+ const value = format[property] ?? DEFAULTS[property];
82
+ if (used.has(value)) {
83
+ throw xsltError("XTSE1300", `The decimal format uses ${value} twice`);
84
+ }
85
+ used.add(value);
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Collects the decimal formats.
91
+ * @param {object[]} declarations
92
+ * @param {object} cx
93
+ * @returns {object[]} definitions with a `name` (Q{uri}local, "" for the
94
+ * default format)
95
+ */
96
+ export function collectDecimalFormats(declarations, cx) {
97
+ const formats = new Map();
98
+ for (const { element, precedence } of declarations) {
99
+ const nameText = element.getAttribute("name");
100
+ const name = nameText
101
+ ? `Q${clarkOf(cx.exprs.qname(nameText, element))}`
102
+ : "";
103
+ const format = formats.get(name) ?? { name, from: {}, conflict: {} };
104
+ for (const property of [...CHARACTERS, ...STRINGS]) {
105
+ if (!element.hasAttribute(property)) continue;
106
+ const value = element.getAttribute(property);
107
+ if (CHARACTERS.includes(property) && [...value].length !== 1) {
108
+ throw xsltError("XTSE0020", `${property} must be one character`);
109
+ }
110
+ if (format.from[property] === precedence && format[property] !== value) {
111
+ format.conflict[property] = precedence;
112
+ }
113
+ if (
114
+ format.from[property] === undefined ||
115
+ format.from[property] <= precedence
116
+ ) {
117
+ format[property] = value;
118
+ format.from[property] = precedence;
119
+ }
120
+ }
121
+ formats.set(name, format);
122
+ }
123
+ for (const format of formats.values()) {
124
+ for (const [property, precedence] of Object.entries(format.conflict)) {
125
+ if (format.from[property] === precedence) {
126
+ throw xsltError("XTSE1290", `Conflicting values of ${property}`);
127
+ }
128
+ }
129
+ checkFormat(format);
130
+ }
131
+ return [...formats.values()];
132
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * The top-level declarations of a stylesheet module tree, grouped by kind
3
+ * and checked: known XSLT declarations (XTSE0010), elements in no
4
+ * namespace (XTSE0130), xsl:expose only in xsl:package, empty
5
+ * declarations (XTSE0260), no xsl:import-schema (XTSE1650: this
6
+ * processor is not schema aware).
7
+ *
8
+ * @module @tradik/xslt3/xslt/compiler/declarationKinds
9
+ */
10
+
11
+ import { isXsl, XSL_NS, xsltError } from "../names.js";
12
+ import { checkAttributes, checkEmpty } from "./attributes.js";
13
+ import { infoOf } from "./elementInfo.js";
14
+ import { declarationsOf } from "./modules.js";
15
+
16
+ /** Top-level XSLT declarations. */
17
+ const DECLARATIONS = new Set([
18
+ "template",
19
+ "function",
20
+ "variable",
21
+ "param",
22
+ "key",
23
+ "output",
24
+ "decimal-format",
25
+ "namespace-alias",
26
+ "strip-space",
27
+ "preserve-space",
28
+ "character-map",
29
+ "attribute-set",
30
+ "mode",
31
+ "import-schema",
32
+ "global-context-item",
33
+ "accumulator",
34
+ "use-package",
35
+ "expose",
36
+ ]);
37
+
38
+ /** Declarations that must be empty. */
39
+ const EMPTY_DECLARATIONS = new Set([
40
+ "strip-space",
41
+ "preserve-space",
42
+ "namespace-alias",
43
+ "output",
44
+ "decimal-format",
45
+ ]);
46
+
47
+ /**
48
+ * The declarations of a module tree by kind, checked.
49
+ * @param {object} tree - See modules.js
50
+ * @param {object} cx - Stylesheet compiler (records the precedences)
51
+ * @returns {(kind: string) => object[]} the declarations of a kind
52
+ */
53
+ export function collectDeclarations(tree, cx) {
54
+ const packageRoot = isXsl(tree.root, "package") ? tree.root : null;
55
+ const byKind = new Map();
56
+ for (const declaration of declarationsOf(tree)) {
57
+ const { element, simplified } = declaration;
58
+ const kind = simplified ? "simplified" : element.localName;
59
+ if (!simplified && element.namespaceURI !== XSL_NS) {
60
+ if (!element.namespaceURI) {
61
+ throw xsltError("XTSE0130", `${element.nodeName} has no namespace`);
62
+ }
63
+ continue;
64
+ }
65
+ if (!simplified && !DECLARATIONS.has(kind)) {
66
+ if (infoOf(element).version > 3) continue;
67
+ throw xsltError("XTSE0010", `xsl:${kind} is not a declaration`);
68
+ }
69
+ if (kind === "expose" && element.parentNode !== packageRoot) {
70
+ throw xsltError("XTSE0010", "xsl:expose belongs in xsl:package");
71
+ }
72
+ if (!simplified) checkAttributes(element);
73
+ if (EMPTY_DECLARATIONS.has(kind)) checkEmpty(element, cx);
74
+ if (!byKind.has(kind)) byKind.set(kind, []);
75
+ byKind.get(kind).push(declaration);
76
+ cx.declarationPrecedence.set(element, declaration.precedence);
77
+ }
78
+ if (byKind.has("import-schema")) {
79
+ throw xsltError("XTSE1650", "xsl:import-schema needs schema awareness");
80
+ }
81
+ return (kind) => byKind.get(kind) ?? [];
82
+ }
@@ -0,0 +1,246 @@
1
+ /**
2
+ * What a stylesheet element inherits from its ancestors: in-scope
3
+ * namespaces, the effective version, the standard attributes
4
+ * xpath-default-namespace, default-collation, exclude-result-prefixes,
5
+ * extension-element-prefixes and expand-text, and the base URI (xml:base).
6
+ * Computed once per element and cached.
7
+ *
8
+ * @module @tradik/xslt3/xslt/compiler/elementInfo
9
+ */
10
+
11
+ import { getCollation } from "../../functions/collations.js";
12
+ import { resolveUri } from "../../xpath/eval/uris.js";
13
+ import {
14
+ clarkOf,
15
+ RESERVED_NAMESPACES,
16
+ resolveQName,
17
+ standardAttr,
18
+ tokens,
19
+ XML_NS,
20
+ XMLNS_NS,
21
+ XSL_NS,
22
+ xsltError,
23
+ } from "../names.js";
24
+
25
+ /**
26
+ * @typedef {object} ElementInfo
27
+ * @property {Map<string, string>} namespaces - Prefix to URI ("" default)
28
+ * @property {number} version - Effective version
29
+ * @property {string} xpathDefaultNs - Default element namespace of XPath
30
+ * @property {string|undefined} defaultCollation - Collation URI
31
+ * @property {string|undefined} baseUri
32
+ * @property {Set<string>} excluded - Excluded result namespace URIs
33
+ * @property {Set<string>} extension - Extension namespace URIs
34
+ * @property {boolean} expandText - Text value templates enabled
35
+ * @property {string} defaultMode - Clark name, "" for the unnamed mode
36
+ */
37
+
38
+ /** @type {WeakMap<Element, ElementInfo>} */
39
+ const cache = new WeakMap();
40
+ /** @type {WeakMap<Element, string>} base URIs of module roots */
41
+ const moduleUris = new WeakMap();
42
+
43
+ /**
44
+ * Records the URI of a stylesheet module, the base URI of its root.
45
+ * @param {Element} root
46
+ * @param {string|undefined} uri
47
+ */
48
+ export function setModuleUri(root, uri) {
49
+ if (uri !== undefined) moduleUris.set(root, uri);
50
+ }
51
+
52
+ /**
53
+ * Forgets the info of an element whose attributes changed (shadow
54
+ * attributes evaluated after its use-when condition read its info).
55
+ * @param {Element} element
56
+ */
57
+ export function forgetInfo(element) {
58
+ cache.delete(element);
59
+ }
60
+
61
+ /**
62
+ * The URI of the stylesheet module an element belongs to.
63
+ * @param {Node} element
64
+ * @returns {string|undefined}
65
+ */
66
+ export function moduleUriOf(element) {
67
+ for (let node = element; node?.nodeType === 1; node = node.parentNode) {
68
+ if (moduleUris.has(node)) return moduleUris.get(node);
69
+ }
70
+ return undefined;
71
+ }
72
+
73
+ const ROOT_INFO = Object.freeze({
74
+ namespaces: new Map([["xml", XML_NS]]),
75
+ version: 3,
76
+ xpathDefaultNs: "",
77
+ defaultCollation: undefined,
78
+ baseUri: undefined,
79
+ excluded: new Set([XSL_NS]),
80
+ extension: new Set(),
81
+ expandText: false,
82
+ defaultMode: "",
83
+ });
84
+
85
+ /**
86
+ * @param {Element} element
87
+ * @param {Map<string, string>} inherited
88
+ * @returns {Map<string, string>} the in-scope namespaces of the element
89
+ */
90
+ function namespacesOf(element, inherited) {
91
+ let namespaces = inherited;
92
+ for (const attribute of element.attributes) {
93
+ const name = attribute.name;
94
+ const declaration =
95
+ attribute.namespaceURI === XMLNS_NS ||
96
+ name === "xmlns" ||
97
+ name.startsWith("xmlns:");
98
+ if (!declaration) continue;
99
+ if (namespaces === inherited) namespaces = new Map(inherited);
100
+ namespaces.set(name === "xmlns" ? "" : name.slice(6), attribute.value);
101
+ }
102
+ if (element.namespaceURI && !namespaces.has(element.prefix ?? "")) {
103
+ if (namespaces === inherited) namespaces = new Map(inherited);
104
+ namespaces.set(element.prefix ?? "", element.namespaceURI);
105
+ }
106
+ return namespaces;
107
+ }
108
+
109
+ /**
110
+ * URIs of a list of prefixes (exclude-result-prefixes and friends).
111
+ * @param {string} text
112
+ * @param {Map<string, string>} namespaces
113
+ * @param {Set<string>} into - Receives the URIs
114
+ * @param {string} code - Error code for an undeclared prefix
115
+ */
116
+ function addPrefixUris(text, namespaces, into, code) {
117
+ for (const token of tokens(text)) {
118
+ if (token === "#all") {
119
+ for (const uri of namespaces.values()) into.add(uri);
120
+ continue;
121
+ }
122
+ const prefix = token === "#default" ? "" : token;
123
+ const uri = namespaces.get(prefix);
124
+ if (uri === undefined || (uri === "" && prefix !== "")) {
125
+ throw xsltError(
126
+ prefix === "" ? "XTSE0809" : code,
127
+ `No namespace is bound to the prefix "${token}"`,
128
+ );
129
+ }
130
+ into.add(uri);
131
+ }
132
+ }
133
+
134
+ /**
135
+ * The first collation of a default-collation list that is supported.
136
+ * @param {string} text
137
+ * @param {string|undefined} base
138
+ * @returns {string}
139
+ */
140
+ function chooseCollation(text, base) {
141
+ for (const token of tokens(text)) {
142
+ const uri = resolveUri(token, base);
143
+ try {
144
+ getCollation(uri);
145
+ return uri;
146
+ } catch {
147
+ // try the next one
148
+ }
149
+ }
150
+ throw xsltError("XTSE0125", `No supported collation in "${text}"`);
151
+ }
152
+
153
+ /**
154
+ * The parsed version attribute.
155
+ * @param {string} text
156
+ * @returns {number}
157
+ */
158
+ function parseVersion(text) {
159
+ if (!/^\s*\+?(\d+(\.\d*)?|\.\d+)\s*$/.test(text)) {
160
+ throw xsltError("XTSE0110", `Invalid version "${text}"`);
161
+ }
162
+ return Number(text);
163
+ }
164
+
165
+ /**
166
+ * Computes the info of an element from the info of its parent.
167
+ * @param {Element} element
168
+ * @param {ElementInfo} parent
169
+ * @returns {ElementInfo}
170
+ */
171
+ function computeInfo(element, parent) {
172
+ const info = { ...parent };
173
+ info.namespaces = namespacesOf(element, parent.namespaces);
174
+ const own = (name) => standardAttr(element, name);
175
+ const xmlBase = element.getAttributeNS(XML_NS, "base");
176
+ const moduleUri = moduleUris.get(element);
177
+ if (moduleUri !== undefined) info.baseUri = moduleUri;
178
+ if (xmlBase) info.baseUri = resolveUri(xmlBase, info.baseUri);
179
+ const version = own("version");
180
+ if (version !== undefined) info.version = parseVersion(version);
181
+ const xpathNs = own("xpath-default-namespace");
182
+ if (xpathNs !== undefined) info.xpathDefaultNs = xpathNs.trim();
183
+ const collation = own("default-collation");
184
+ if (collation !== undefined) {
185
+ info.defaultCollation = chooseCollation(collation, info.baseUri);
186
+ }
187
+ const excluded = own("exclude-result-prefixes");
188
+ const extension = own("extension-element-prefixes");
189
+ if (excluded !== undefined || extension !== undefined) {
190
+ info.excluded = new Set(parent.excluded);
191
+ addPrefixUris(excluded, info.namespaces, info.excluded, "XTSE0808");
192
+ }
193
+ if (extension !== undefined) {
194
+ info.extension = new Set(parent.extension);
195
+ addPrefixUris(extension, info.namespaces, info.extension, "XTSE1430");
196
+ for (const uri of info.extension) {
197
+ if (RESERVED_NAMESPACES.has(uri)) {
198
+ throw xsltError("XTSE0085", `${uri} cannot be an extension namespace`);
199
+ }
200
+ }
201
+ for (const uri of info.extension) info.excluded.add(uri);
202
+ }
203
+ const defaultMode = own("default-mode");
204
+ if (defaultMode !== undefined) {
205
+ const text = defaultMode.trim();
206
+ info.defaultMode =
207
+ text === "#unnamed"
208
+ ? ""
209
+ : clarkOf(resolveQName(text, info.namespaces, { code: "XTSE0545" }));
210
+ }
211
+ const expand = own("expand-text");
212
+ if (expand !== undefined) {
213
+ const value = expand.trim();
214
+ if (!["yes", "true", "1", "no", "false", "0"].includes(value)) {
215
+ throw xsltError("XTSE0020", `Invalid expand-text "${expand}"`);
216
+ }
217
+ info.expandText = ["yes", "true", "1"].includes(value);
218
+ }
219
+ return info;
220
+ }
221
+
222
+ /**
223
+ * The info of a stylesheet element.
224
+ * @param {Element} element
225
+ * @returns {ElementInfo}
226
+ */
227
+ export function infoOf(element) {
228
+ let info = cache.get(element);
229
+ if (info) return info;
230
+ const chain = [];
231
+ let current = element;
232
+ while (current && current.nodeType === 1 && !cache.has(current)) {
233
+ chain.push(current);
234
+ if (moduleUris.has(current)) break;
235
+ current = current.parentNode;
236
+ }
237
+ info =
238
+ current && current.nodeType === 1 && cache.has(current)
239
+ ? cache.get(current)
240
+ : ROOT_INFO;
241
+ for (let i = chain.length - 1; i >= 0; i--) {
242
+ info = computeInfo(chain[i], info);
243
+ cache.set(chain[i], info);
244
+ }
245
+ return info;
246
+ }
@@ -0,0 +1,175 @@
1
+ /**
2
+ * XPath in a stylesheet: the static context of each stylesheet element
3
+ * (in-scope namespaces, xpath-default-namespace, base URI, backwards
4
+ * compatibility, decimal formats, the function library with the
5
+ * stylesheet functions), and the compilation of expressions, sequence
6
+ * types and attribute value templates against the variables in scope.
7
+ *
8
+ * @module @tradik/xslt3/xslt/compiler/expressions
9
+ */
10
+
11
+ import { compileNode, withinLimits } from "../../xpath/eval/compiler.js";
12
+ import { compileSequenceType } from "../../xpath/eval/sequenceType.js";
13
+ import { createStaticContext } from "../../xpath/eval/staticContext.js";
14
+ import { parseXPath } from "../../xpath/syntax/index.js";
15
+ import { TokenStream } from "../../xpath/syntax/tokenStream.js";
16
+ import { parseSequenceType } from "../../xpath/syntax/types.js";
17
+ import { XPathError } from "../../errors.js";
18
+ import { resolveQName } from "../names.js";
19
+ import { parseAvt } from "./avt.js";
20
+ import { infoOf } from "./elementInfo.js";
21
+
22
+ /**
23
+ * A compiled XPath expression of the stylesheet.
24
+ * @typedef {object} Expr
25
+ * @property {(ctx: object) => Array} run - The evaluator
26
+ * @property {string} text - Source text
27
+ * @property {object} sc - Static context
28
+ * @property {object} ast - Syntax tree
29
+ */
30
+
31
+ /** The namespace of the standard functions. */
32
+ const FN_NS = "http://www.w3.org/2005/xpath-functions";
33
+
34
+ /**
35
+ * In backwards-compatible mode a call of an unknown extension function
36
+ * is an error only when evaluated (XSLT 3.0 section 24.1.2, XTDE1425).
37
+ * @param {Error} error - Raised by the XPath compiler
38
+ * @param {object} sc - Static context of the expression
39
+ * @returns {(ctx: object) => Array} an evaluator raising XTDE1425
40
+ * @throws {Error} the error otherwise
41
+ */
42
+ function unavailableFunction(error, sc) {
43
+ const uri = /^XPST0017: Unknown function Q\{([^}]*)\}/.exec(
44
+ error.message,
45
+ )?.[1];
46
+ if (!sc.backwardsCompatible || uri === undefined || uri === FN_NS) {
47
+ throw error;
48
+ }
49
+ return () => {
50
+ throw new XPathError("XTDE1425", error.message.slice(10));
51
+ };
52
+ }
53
+
54
+ /** Compiles the XPath expressions of a stylesheet. */
55
+ export class ExpressionCompiler {
56
+ /**
57
+ * @param {object} options
58
+ * @param {import("../../functions/registry.js").FunctionLibrary} options.library
59
+ * @param {Array<object>} [options.decimalFormats] - Definitions
60
+ * @param {object} [options.owner] - Package compiler of the expressions
61
+ */
62
+ constructor({ library, decimalFormats = [], owner = null }) {
63
+ this.library = library;
64
+ this.decimalFormats = decimalFormats;
65
+ /** The package compiler whose keys key() reads (null: the stylesheet's) */
66
+ this.owner = owner;
67
+ /** @type {WeakMap<Map, Map<string, object>>} */
68
+ this.cache = new WeakMap();
69
+ }
70
+
71
+ /**
72
+ * The static context of the expressions of a stylesheet element.
73
+ * @param {Element} element
74
+ * @returns {object} an XPath static context
75
+ */
76
+ staticContext(element) {
77
+ const info = infoOf(element);
78
+ let byKey = this.cache.get(info.namespaces);
79
+ if (!byKey) {
80
+ byKey = new Map();
81
+ this.cache.set(info.namespaces, byKey);
82
+ }
83
+ const compatible = info.version < 2;
84
+ const key = `${info.xpathDefaultNs}|${info.baseUri}|${compatible}`;
85
+ let sc = byKey.get(key);
86
+ if (!sc) {
87
+ const namespaces = new Map(
88
+ [...info.namespaces].filter(([p]) => p !== ""),
89
+ );
90
+ sc = createStaticContext(
91
+ {
92
+ namespaces,
93
+ defaultElementNamespace: info.xpathDefaultNs,
94
+ baseUri: info.baseUri,
95
+ backwardsCompatible: compatible,
96
+ decimalFormats: this.decimalFormats,
97
+ },
98
+ this.library,
99
+ );
100
+ sc.owner = this.owner;
101
+ // for element-available(), whose unprefixed names use it
102
+ sc.defaultNamespace = info.namespaces.get("") ?? "";
103
+ byKey.set(key, sc);
104
+ }
105
+ return sc;
106
+ }
107
+
108
+ /**
109
+ * Compiles an XPath expression written on a stylesheet element.
110
+ * @param {string} text
111
+ * @param {Element} element
112
+ * @param {object|null} vars - Variables in scope (see eval/scope.js)
113
+ * @returns {Expr}
114
+ */
115
+ xpath(text, element, vars) {
116
+ const sc = this.staticContext(element);
117
+ const ast = parseXPath(text);
118
+ const run = withinLimits(() => {
119
+ try {
120
+ return compileNode(ast, { sc, vars });
121
+ } catch (error) {
122
+ return unavailableFunction(error, sc);
123
+ }
124
+ });
125
+ return { run, text, sc, ast };
126
+ }
127
+
128
+ /**
129
+ * Compiles an XPath AST (patterns compile their parts this way).
130
+ * @param {object} ast
131
+ * @param {object} sc
132
+ * @param {object|null} vars
133
+ * @returns {(ctx: object) => Array}
134
+ */
135
+ compileAst(ast, sc, vars) {
136
+ return withinLimits(() => compileNode(ast, { sc, vars }));
137
+ }
138
+
139
+ /**
140
+ * Compiles a sequence type (an `as` attribute).
141
+ * @param {string} text
142
+ * @param {Element} element
143
+ * @returns {import("../../xpath/eval/sequenceType.js").SequenceType}
144
+ */
145
+ sequenceType(text, element) {
146
+ const tokens = new TokenStream(text);
147
+ const node = parseSequenceType(tokens);
148
+ if (tokens.peek().type !== "eof") tokens.fail("Unexpected token");
149
+ return compileSequenceType(node, this.staticContext(element));
150
+ }
151
+
152
+ /**
153
+ * Compiles an attribute value template.
154
+ * @param {string} text
155
+ * @param {Element} element
156
+ * @param {object|null} vars
157
+ * @returns {Array<string|Expr>} literal parts and expressions
158
+ */
159
+ avt(text, element, vars) {
160
+ return parseAvt(text).map((part) =>
161
+ typeof part === "string" ? part : this.xpath(part.expr, element, vars),
162
+ );
163
+ }
164
+
165
+ /**
166
+ * Resolves a QName attribute value of a stylesheet element.
167
+ * @param {string} text
168
+ * @param {Element} element
169
+ * @param {object} [options] - See names.resolveQName
170
+ * @returns {import("../names.js").ExpandedName}
171
+ */
172
+ qname(text, element, options) {
173
+ return resolveQName(text, infoOf(element).namespaces, options);
174
+ }
175
+ }