@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,151 @@
1
+ /**
2
+ * Patterns (XSLT 3.0 section 5.5): parsed as XPath expressions and
3
+ * compiled to matchers. A union is split into alternatives, each with its
4
+ * default priority (section 6.5); intersect and except combine patterns,
5
+ * and predicate patterns `.[P]` match any item.
6
+ *
7
+ * @module @tradik/xslt3/xslt/patterns/compile
8
+ */
9
+
10
+ import { parseXPath } from "../../xpath/syntax/index.js";
11
+ import { xsltError } from "../names.js";
12
+ import { parentOf } from "../../xpath/eval/domNodes.js";
13
+ import { derive } from "../runtime/context.js";
14
+ import { compileGeneralPath } from "./general.js";
15
+ import { checkPattern, isPredicatePattern } from "./grammar.js";
16
+ import { checkGroupingCalls } from "./patternChecks.js";
17
+ import { compilePathPattern } from "./paths.js";
18
+
19
+ /**
20
+ * @typedef {object} PatternAlternative
21
+ * @property {(item: *, xc: object) => boolean} matches
22
+ * @property {number} priority - Default priority
23
+ * @property {string} key - Index key: "e{uri}local", "a{uri}local", "k"
24
+ * followed by a nodeType, or "*" for any item
25
+ */
26
+
27
+ /**
28
+ * Splits a pattern into its union alternatives.
29
+ * @param {object} ast
30
+ * @returns {object[]}
31
+ */
32
+ function alternativesOf(ast) {
33
+ if (ast.type === "SetExpr" && ast.operator === "union") {
34
+ return [...alternativesOf(ast.left), ...alternativesOf(ast.right)];
35
+ }
36
+ return [ast];
37
+ }
38
+
39
+ /**
40
+ * Compiles a predicate pattern `.` or `.[P]` (XSLT 3.0), which matches
41
+ * any item.
42
+ * @param {object} ast
43
+ * @param {object} env
44
+ * @returns {PatternAlternative}
45
+ */
46
+ function compilePredicatePattern(ast, env) {
47
+ const run = env.cx.exprs.compileAst(ast, env.sc, env.vars);
48
+ return {
49
+ matches: (item, xc) =>
50
+ run(env.cx.patternContext(item, xc, env.local)).length > 0,
51
+ priority: ast.type === "ContextItemExpr" ? -1 : 1,
52
+ key: "*",
53
+ };
54
+ }
55
+
56
+ /**
57
+ * Compiles `A intersect B` or `A except B` (XSLT 3.0).
58
+ * @param {object} ast
59
+ * @param {object} env
60
+ * @returns {PatternAlternative}
61
+ */
62
+ function compileSetPattern(ast, env) {
63
+ const side = (part) =>
64
+ alternativesOf(part).map((alternative) =>
65
+ compileAlternative(alternative, env),
66
+ );
67
+ const left = side(ast.left);
68
+ const right = side(ast.right);
69
+ const any = (list, item, xc) => list.some((a) => a.matches(item, xc));
70
+ const intersect = ast.operator === "intersect";
71
+ // a node with a parent: both sides are evaluated from the same context
72
+ const formal = compileGeneralPath(ast, env);
73
+ return {
74
+ matches: (item, xc) =>
75
+ parentOf(item)
76
+ ? formal(item, xc)
77
+ : any(left, item, xc) && any(right, item, xc) === intersect,
78
+ priority: 0.5,
79
+ key: left.length === 1 ? left[0].key : "*",
80
+ };
81
+ }
82
+
83
+ /**
84
+ * Compiles one alternative; while it is tested, current() is the item
85
+ * being matched, and a dynamic error is a non-match (XSLT 3.0 5.5.4).
86
+ * @param {object} ast
87
+ * @param {object} env - `{cx, sc, vars, local, text}`
88
+ * @returns {PatternAlternative}
89
+ */
90
+ function compileAlternative(ast, env) {
91
+ let alternative;
92
+ if (ast.type === "SetExpr") alternative = compileSetPattern(ast, env);
93
+ else if (isPredicatePattern(ast)) {
94
+ alternative = compilePredicatePattern(ast, env);
95
+ } else alternative = compilePathPattern(ast, env);
96
+ const { matches } = alternative;
97
+ alternative.matches = (item, xc) => {
98
+ try {
99
+ return matches(item, xc.item === item ? xc : derive(xc, { item }));
100
+ } catch (error) {
101
+ if (!error.code || error.code === "XTDE0640") throw error;
102
+ return false;
103
+ }
104
+ };
105
+ return alternative;
106
+ }
107
+
108
+ /**
109
+ * Compiles a pattern written on a stylesheet element.
110
+ * @param {string} text
111
+ * @param {Element} element
112
+ * @param {object} cx - Stylesheet compiler
113
+ * @param {object|null} [vars] - Local variables in scope (patterns of
114
+ * instructions); template and key patterns see the global ones only
115
+ * @returns {{text: string, alternatives: PatternAlternative[]}}
116
+ * @throws {import("../../errors.js").XPathError} XTSE0340 for an
117
+ * expression that is not a pattern
118
+ */
119
+ export function compilePattern(text, element, cx, vars) {
120
+ let ast;
121
+ try {
122
+ ast = parseXPath(text);
123
+ } catch (error) {
124
+ throw error.code === "XPST0003"
125
+ ? xsltError("XTSE0340", `Invalid pattern "${text}": ${error.message}`)
126
+ : error;
127
+ }
128
+ checkPattern(ast, text);
129
+ checkGroupingCalls(ast);
130
+ const env = {
131
+ cx,
132
+ sc: cx.exprs.staticContext(element),
133
+ vars: vars === undefined ? cx.globalScope().vars : vars,
134
+ local: vars !== undefined,
135
+ text,
136
+ };
137
+ const alternatives = alternativesOf(ast).map((alternative) =>
138
+ compileAlternative(alternative, env),
139
+ );
140
+ return { text, alternatives };
141
+ }
142
+
143
+ /**
144
+ * Whether an item matches any alternative of a pattern.
145
+ * @param {{alternatives: PatternAlternative[]}} pattern
146
+ * @param {*} item
147
+ * @param {object} xc
148
+ * @returns {boolean}
149
+ */
150
+ export const patternMatches = (pattern, item, xc) =>
151
+ pattern.alternatives.some((alternative) => alternative.matches(item, xc));
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Path patterns of any form (XSLT 3.0 section 5.5.3), matched by their
3
+ * formal meaning: a node N matches when it is selected by the equivalent
4
+ * expression evaluated from some node of its tree. The steps are read
5
+ * from the last one: for each step, the context nodes from which the
6
+ * step can select N (the parent for child::, the ancestors for
7
+ * descendant::...) are evaluated, and those that do select N carry on
8
+ * to the step before. Used for the patterns the fast matcher of paths.js
9
+ * does not handle: descendant axes, parenthesized steps, filtered rooted
10
+ * steps, positional predicates over whole paths.
11
+ *
12
+ * @module @tradik/xslt3/xslt/patterns/general
13
+ */
14
+
15
+ import { parentOf } from "../../xpath/eval/domNodes.js";
16
+ import { compileAxisOrigins, filterAlone } from "./axisOrigins.js";
17
+ import { isRootedStep, unfiltered } from "./grammar.js";
18
+
19
+ /** @param {*} node @returns {boolean} whether a node is a document node */
20
+ const isDocument = (node) => node?.nodeType === 9 || node?.nodeType === 11;
21
+
22
+ /** @param {*} item @returns {boolean} */
23
+ const isNode = (item) => typeof item?.nodeType === "number";
24
+
25
+ /**
26
+ * Compiles one step: `origins(node, xc)` returns the context nodes from
27
+ * which the step selects the node.
28
+ * @param {object} step
29
+ * @param {object} env - See compile.js
30
+ * @param {boolean} first - First step of a relative pattern
31
+ * @returns {{origins: (node: Node, xc: object) => Node[]}}
32
+ */
33
+ function compileStep(step, env, first) {
34
+ const run = env.cx.exprs.compileAst(step, env.sc, env.vars);
35
+ const selects = (context, node, xc) =>
36
+ run(env.cx.patternContext(context, xc, env.local)).includes(node);
37
+ if (step.type === "AxisStep") {
38
+ return compileAxisOrigins(step, env, first, run);
39
+ }
40
+ if (step.type === "FilterExpr") {
41
+ const inner = compileStep(unfiltered(step), env, first);
42
+ const predicates = [];
43
+ for (let s = step; s.type === "FilterExpr"; s = s.base) {
44
+ predicates.unshift(s.predicate);
45
+ }
46
+ const alone = filterAlone(predicates, env);
47
+ return {
48
+ origins: (node, xc) =>
49
+ inner.origins(node, xc).flatMap((c) => {
50
+ // a parentless node selected by itself (child-or-top)
51
+ if (c === node && !parentOf(node)) return alone(node, xc);
52
+ return selects(c, node, xc) ? [c] : [];
53
+ }),
54
+ };
55
+ }
56
+ if (step.type === "SetExpr") {
57
+ // each side is a path of its own (its first step adjusted)
58
+ const sides = [step.left, step.right].map((s) => compileStep(s, env, true));
59
+ const union = step.operator === "union";
60
+ return {
61
+ origins: (node, xc) => {
62
+ const candidates = new Set(sides[0].origins(node, xc));
63
+ if (union) {
64
+ for (const c of sides[1].origins(node, xc)) candidates.add(c);
65
+ }
66
+ return [...candidates].filter((c) => selects(c, node, xc));
67
+ },
68
+ };
69
+ }
70
+ if (step.type === "PathExpr" && !step.absolute) {
71
+ return compilePath(step, env, true);
72
+ }
73
+ // an absolute path or a rooted step: any node of the tree is a context
74
+ return { origins: (node, xc) => (selects(node, node, xc) ? [node] : []) };
75
+ }
76
+
77
+ /**
78
+ * Compiles the steps of a relative path.
79
+ * @param {{steps: object[]}} path
80
+ * @param {object} env
81
+ * @param {boolean} first - The path starts a relative pattern
82
+ * @returns {{origins: (node: Node, xc: object) => Node[]}}
83
+ */
84
+ function compilePath(path, env, first) {
85
+ const steps = path.steps.map((step, i) =>
86
+ compileStep(step, env, first && i === 0),
87
+ );
88
+ return {
89
+ origins(node, xc) {
90
+ let current = [node];
91
+ for (let k = steps.length - 1; k >= 0 && current.length > 0; k--) {
92
+ const next = new Set();
93
+ for (const n of current) {
94
+ for (const c of steps[k].origins(n, xc)) next.add(c);
95
+ }
96
+ current = [...next];
97
+ }
98
+ return current;
99
+ },
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Compiles a path pattern of any form.
105
+ * @param {object} ast - PathExpr or a single step
106
+ * @param {object} env - See compile.js
107
+ * @returns {(item: *, xc: object) => boolean}
108
+ */
109
+ export function compileGeneralPath(ast, env) {
110
+ const steps = ast.type === "PathExpr" ? ast.steps : [ast];
111
+ const absolute = ast.type === "PathExpr" && ast.absolute;
112
+ const rootStep = !absolute && isRootedStep(steps[0]) ? steps[0] : null;
113
+ const rest = compilePath(
114
+ { steps: rootStep ? steps.slice(1) : steps },
115
+ env,
116
+ !absolute && !rootStep,
117
+ );
118
+ const root = rootStep
119
+ ? env.cx.exprs.compileAst(rootStep, env.sc, env.vars)
120
+ : null;
121
+ return (item, xc) => {
122
+ if (!isNode(item)) return false;
123
+ const origins = rest.origins(item, xc);
124
+ if (absolute) {
125
+ return origins.some((c) => isDocument(c) && !parentOf(c));
126
+ }
127
+ if (!root) return origins.length > 0;
128
+ if (origins.length === 0) return false;
129
+ const roots = root(env.cx.patternContext(item, xc, env.local));
130
+ return origins.some((c) => roots.includes(c));
131
+ };
132
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * The grammar of patterns (XSLT 3.0 section 5.5.2): a pattern is parsed
3
+ * as an XPath expression, then checked against the production Pattern30:
4
+ * a predicate pattern `.[P]...`, or unions, intersections and
5
+ * differences of path patterns whose steps are forward axis steps,
6
+ * parenthesized patterns with predicates, and rooted first steps
7
+ * (`$var`, doc(), id(), element-with-id(), key(), root()) whose arguments
8
+ * are variable references or literals.
9
+ *
10
+ * @module @tradik/xslt3/xslt/patterns/grammar
11
+ */
12
+
13
+ import { NS } from "../../functions/signatures.js";
14
+ import { withoutComments } from "../compiler/avt.js";
15
+ import { xsltError } from "../names.js";
16
+
17
+ /** Axes allowed in patterns. */
18
+ const FORWARD_AXES = new Set([
19
+ "child",
20
+ "descendant",
21
+ "attribute",
22
+ "self",
23
+ "descendant-or-self",
24
+ "namespace",
25
+ ]);
26
+
27
+ /** Functions allowed as the root of a path pattern, by local name. */
28
+ const ROOT_FUNCTIONS = new Set(["doc", "id", "element-with-id", "key", "root"]);
29
+
30
+ /** Argument kinds allowed in those calls. */
31
+ const ARGUMENTS = new Set(["VarRef", "StringLiteral", "NumericLiteral"]);
32
+
33
+ /**
34
+ * @param {string} message
35
+ * @returns {Error} XTSE0340
36
+ */
37
+ const invalid = (message) => xsltError("XTSE0340", message);
38
+
39
+ /**
40
+ * Removes the predicates of a filter expression.
41
+ * @param {object} ast
42
+ * @returns {object} the filtered expression
43
+ */
44
+ export function unfiltered(ast) {
45
+ let base = ast;
46
+ while (base.type === "FilterExpr") base = base.base;
47
+ return base;
48
+ }
49
+
50
+ /**
51
+ * Whether an expression is a predicate pattern `.[P]...`.
52
+ * @param {object} ast
53
+ * @returns {boolean}
54
+ */
55
+ export const isPredicatePattern = (ast) =>
56
+ unfiltered(ast).type === "ContextItemExpr";
57
+
58
+ /**
59
+ * Whether a call is a rooted step: one of the allowed functions in the
60
+ * standard function namespace.
61
+ * @param {object} ast - A FunctionCall
62
+ * @returns {boolean}
63
+ */
64
+ function isRootFunction(ast) {
65
+ const { prefix, uri, local } = ast.name;
66
+ const standard =
67
+ uri === NS.fn || (uri === null && (prefix === null || prefix === "fn"));
68
+ return standard && ROOT_FUNCTIONS.has(local);
69
+ }
70
+
71
+ /**
72
+ * Checks a rooted step `$v[...]` or `f(...)[...]`.
73
+ * @param {object} ast
74
+ */
75
+ function checkRooted(ast) {
76
+ const base = unfiltered(ast);
77
+ if (base.type === "VarRef") return;
78
+ if (!isRootFunction(base)) {
79
+ throw invalid(`${base.name.local}() cannot start a pattern`);
80
+ }
81
+ if (base.name.local === "root" && base.arguments.length > 0) {
82
+ throw invalid("root() in a pattern takes no argument");
83
+ }
84
+ for (const argument of base.arguments) {
85
+ if (!ARGUMENTS.has(argument.type)) {
86
+ throw invalid("Arguments in a pattern must be variables or literals");
87
+ }
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Whether a step is rooted (a variable or a function call, filtered).
93
+ * @param {object} ast
94
+ * @returns {boolean}
95
+ */
96
+ export const isRootedStep = (ast) => {
97
+ const type = unfiltered(ast).type;
98
+ return type === "VarRef" || type === "FunctionCall";
99
+ };
100
+
101
+ /**
102
+ * Checks one step of a path pattern.
103
+ * @param {object} ast
104
+ * @param {boolean} first - The first step of a relative path
105
+ */
106
+ function checkStep(ast, first) {
107
+ if (ast.type === "AxisStep") {
108
+ if (!FORWARD_AXES.has(ast.axis)) {
109
+ throw invalid(`The ${ast.axis} axis is not allowed in a pattern`);
110
+ }
111
+ return;
112
+ }
113
+ if (isRootedStep(ast)) {
114
+ if (!first) throw invalid("A rooted step must come first in a pattern");
115
+ checkRooted(ast);
116
+ return;
117
+ }
118
+ const base = unfiltered(ast);
119
+ if (
120
+ base.type === "SetExpr" ||
121
+ base.type === "PathExpr" ||
122
+ (base.type === "AxisStep" && base !== ast)
123
+ ) {
124
+ checkUnion(base);
125
+ return;
126
+ }
127
+ throw invalid("Invalid step in a pattern");
128
+ }
129
+
130
+ /**
131
+ * Checks a UnionExprP (no predicate pattern inside).
132
+ * @param {object} ast
133
+ */
134
+ function checkUnion(ast) {
135
+ if (ast.type === "SetExpr") {
136
+ checkUnion(ast.left);
137
+ checkUnion(ast.right);
138
+ return;
139
+ }
140
+ if (ast.type === "PathExpr") {
141
+ ast.steps.forEach((step, i) => checkStep(step, i === 0 && !ast.absolute));
142
+ return;
143
+ }
144
+ checkStep(ast, true);
145
+ }
146
+
147
+ /** Functions a pattern cannot call, with their error codes. */
148
+ const FORBIDDEN = {
149
+ "current-merge-group": "XTSE3470",
150
+ "current-merge-key": "XTSE3500",
151
+ };
152
+
153
+ /**
154
+ * Checks that a pattern calls none of the forbidden functions.
155
+ * @param {*} node - A node of the syntax tree (or any value in it)
156
+ */
157
+ function checkFunctions(node) {
158
+ if (Array.isArray(node)) {
159
+ node.forEach(checkFunctions);
160
+ return;
161
+ }
162
+ if (typeof node !== "object" || node === null) return;
163
+ if (node.type === "FunctionCall" || node.type === "NamedFunctionRef") {
164
+ const code = FORBIDDEN[node.name.local];
165
+ const { uri, prefix } = node.name;
166
+ const standard =
167
+ uri === NS.fn || (uri === null && (prefix ?? "fn") === "fn");
168
+ if (code && standard) {
169
+ throw xsltError(code, `${node.name.local}() cannot be used in a pattern`);
170
+ }
171
+ }
172
+ for (const value of Object.values(node)) checkFunctions(value);
173
+ }
174
+
175
+ /**
176
+ * Checks a pattern.
177
+ * @param {object} ast - The parsed pattern
178
+ * @param {string} text - Its source text
179
+ * @throws {import("../../errors.js").XPathError} XTSE0340
180
+ */
181
+ export function checkPattern(ast, text) {
182
+ checkFunctions(ast);
183
+ if (isPredicatePattern(ast)) {
184
+ if (/\S/.test(withoutComments(text.slice(0, unfiltered(ast).start)))) {
185
+ throw invalid("A predicate pattern cannot be parenthesized");
186
+ }
187
+ return;
188
+ }
189
+ checkUnion(ast);
190
+ }
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Modes and their template rules (XSLT 3.0 sections 6.4 to 6.6): rules
3
+ * ranked by import precedence, priority and declaration order, indexed by
4
+ * the node kind and name their patterns can match, so finding the rule
5
+ * for a node tests only the candidates for that node.
6
+ *
7
+ * @module @tradik/xslt3/xslt/patterns/modes
8
+ */
9
+
10
+ import { xsltError } from "../names.js";
11
+
12
+ /**
13
+ * A template rule: one alternative of the pattern of a template.
14
+ * @typedef {object} Rule
15
+ * @property {object} template - The compiled template
16
+ * @property {import("./compile.js").PatternAlternative} alternative
17
+ * @property {number} priority
18
+ * @property {number} precedence - Import precedence
19
+ * @property {number} position - Declaration order
20
+ * @property {number} [rank] - Position in the ranking (0 is best)
21
+ */
22
+
23
+ /**
24
+ * The index keys of an item: specific (by name) and by kind.
25
+ * @param {*} item
26
+ * @returns {string[]}
27
+ */
28
+ function itemKeys(item) {
29
+ switch (item?.nodeType) {
30
+ case 1:
31
+ return [`e{${item.namespaceURI ?? ""}}${item.localName}`, "k1"];
32
+ case 2:
33
+ return [`a{${item.namespaceURI ?? ""}}${item.localName}`, "k2"];
34
+ case 3:
35
+ case 4:
36
+ return ["k3"];
37
+ case 11:
38
+ return ["k9"];
39
+ case undefined:
40
+ return [];
41
+ default:
42
+ return [`k${item.nodeType}`];
43
+ }
44
+ }
45
+
46
+ /** A mode: its rules and how unmatched items are processed. */
47
+ export class Mode {
48
+ /** @param {string} name - Clark name, "#default" for the unnamed mode */
49
+ constructor(name) {
50
+ this.name = name;
51
+ /** @type {Rule[]} */
52
+ this.rules = [];
53
+ this.onNoMatch = "text-only-copy";
54
+ this.onMultipleMatch = "use-last";
55
+ this.index = null;
56
+ }
57
+
58
+ /** @param {Rule} rule */
59
+ add(rule) {
60
+ this.rules.push(rule);
61
+ this.index = null;
62
+ }
63
+
64
+ /** Ranks the rules and builds the index. */
65
+ build() {
66
+ this.rules.sort(
67
+ (a, b) =>
68
+ b.precedence - a.precedence ||
69
+ b.priority - a.priority ||
70
+ b.position - a.position,
71
+ );
72
+ const index = new Map([["*", []]]);
73
+ this.rules.forEach((rule, rank) => {
74
+ rule.rank = rank;
75
+ const key = rule.alternative.key;
76
+ if (!index.has(key)) index.set(key, []);
77
+ index.get(key).push(rule);
78
+ });
79
+ this.index = index;
80
+ }
81
+
82
+ /**
83
+ * The candidate rules of an item, best first.
84
+ * @param {*} item
85
+ * @returns {Rule[]}
86
+ */
87
+ candidates(item) {
88
+ if (!this.index) {
89
+ this.build();
90
+ this.memo = new Map();
91
+ }
92
+ const keys = itemKeys(item);
93
+ const memoKey = keys[0] ?? "";
94
+ let result = this.memo.get(memoKey);
95
+ if (!result) {
96
+ const lists = [this.index.get("*")];
97
+ for (const key of keys) {
98
+ const list = this.index.get(key);
99
+ if (list) lists.push(list);
100
+ }
101
+ result = lists.flat().sort((a, b) => a.rank - b.rank);
102
+ this.memo.set(memoKey, result);
103
+ }
104
+ return result;
105
+ }
106
+
107
+ /**
108
+ * Finds the best matching rule.
109
+ * @param {*} item
110
+ * @param {object} xc - Context whose item is `item`
111
+ * @param {(rule: Rule) => boolean} [accept] - Restricts the rules
112
+ * (xsl:apply-imports, xsl:next-match)
113
+ * @returns {Rule|null}
114
+ */
115
+ find(item, xc, accept) {
116
+ const candidates = this.candidates(item);
117
+ for (let i = 0; i < candidates.length; i++) {
118
+ const rule = candidates[i];
119
+ if (accept && !accept(rule)) continue;
120
+ if (!rule.alternative.matches(item, xc)) continue;
121
+ if (this.onMultipleMatch === "fail") {
122
+ this.checkAmbiguity(candidates, i, item, xc, accept);
123
+ }
124
+ return rule;
125
+ }
126
+ return null;
127
+ }
128
+
129
+ /**
130
+ * Raises XTDE0540 when another rule of the same rank matches.
131
+ * @param {Rule[]} candidates
132
+ * @param {number} i - Index of the chosen rule
133
+ * @param {*} item
134
+ * @param {object} xc
135
+ * @param {Function} [accept]
136
+ */
137
+ checkAmbiguity(candidates, i, item, xc, accept) {
138
+ const chosen = candidates[i];
139
+ for (let j = i + 1; j < candidates.length; j++) {
140
+ const other = candidates[j];
141
+ if (
142
+ other.precedence !== chosen.precedence ||
143
+ other.priority !== chosen.priority
144
+ ) {
145
+ break;
146
+ }
147
+ if (other.template === chosen.template) continue;
148
+ if ((!accept || accept(other)) && other.alternative.matches(item, xc)) {
149
+ throw xsltError("XTDE0540", "Several template rules match the item");
150
+ }
151
+ }
152
+ }
153
+ }