@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,168 @@
1
+ /**
2
+ * Compilers of function calls (XPath 3.1 section 3.1.5): static calls,
3
+ * partial application, named function references, dynamic calls and the
4
+ * arrow operator.
5
+ *
6
+ * @module @tradik/xslt3/xpath/eval/calls
7
+ */
8
+
9
+ import { XPathError } from "../../errors.js";
10
+ import { FunctionItem } from "../../items/function.js";
11
+ import { isFunctionItem } from "../../xdm/atomic.js";
12
+ import { integerItem } from "./atomics.js";
13
+ import { functionItemOf, resolveFunction } from "./functionItems.js";
14
+ import { contextItem } from "./scope.js";
15
+ import { signatureOf } from "./sequenceType.js";
16
+ import { namespaceOf } from "./staticContext.js";
17
+ import { NS } from "../../functions/signatures.js";
18
+
19
+ const isPlaceholder = (arg) => arg.type === "ArgumentPlaceholder";
20
+
21
+ /**
22
+ * Partial application: a function item taking the placeholder arguments.
23
+ * @param {Array<Function|null>} args - Evaluators, null for placeholders
24
+ * @param {{params: object[], returns: object}|null} signature - Of the
25
+ * target function, null when unknown
26
+ * @param {(ctx: object) => (args: Array<Array>) => Array} target - Gives
27
+ * the function to call
28
+ * @returns {import("./scope.js").Evaluator}
29
+ */
30
+ function partial(args, signature, target) {
31
+ const holes = [];
32
+ args.forEach((arg, i) => arg === null && holes.push(i));
33
+ return (ctx) => {
34
+ const { invoke, signature: actual = signature } = target(ctx);
35
+ const fixed = args.map((arg) => (arg === null ? null : arg(ctx)));
36
+ return [
37
+ new FunctionItem({
38
+ arity: holes.length,
39
+ signature: {
40
+ params: holes.map((i) => actual.params[i]),
41
+ returns: actual.returns,
42
+ },
43
+ invoke: (values) => {
44
+ const all = fixed.slice();
45
+ holes.forEach((hole, i) => (all[hole] = values[i]));
46
+ return invoke(all);
47
+ },
48
+ }),
49
+ ];
50
+ };
51
+ }
52
+
53
+ /**
54
+ * @param {Array} sequence - Value of the function expression
55
+ * @param {number} arity - Number of arguments supplied
56
+ * @returns {*} the function item to call
57
+ * @throws {XPathError} XPTY0004 when it is not a single function of that
58
+ * arity
59
+ */
60
+ function calledFunction(sequence, arity) {
61
+ const [item] = sequence;
62
+ if (sequence.length !== 1 || !isFunctionItem(item)) {
63
+ throw new XPathError(
64
+ "XPTY0004",
65
+ "A dynamic function call needs exactly one function item",
66
+ );
67
+ }
68
+ if (item.arity !== arity) {
69
+ throw new XPathError(
70
+ "XPTY0004",
71
+ `The function has arity ${item.arity}, not ${arity}`,
72
+ );
73
+ }
74
+ return item;
75
+ }
76
+
77
+ /** Compilers by node type. */
78
+ export const callCompilers = {
79
+ FunctionCall(node, scope, compile) {
80
+ const { sc } = scope;
81
+ const uri = namespaceOf(node.name, sc, sc.defaultFunctionNamespace);
82
+ const local = node.name.local;
83
+ const arity = node.arguments.length;
84
+ const resolved = resolveFunction(uri, local, arity, sc);
85
+ const args = node.arguments.map((arg) =>
86
+ isPlaceholder(arg) ? null : compile(arg, scope),
87
+ );
88
+ if (resolved.signature === null) return () => resolved.call();
89
+ if (args.includes(null)) {
90
+ return partial(args, resolved.signature, (ctx) => ({
91
+ invoke: (values) => resolved.call(values, ctx),
92
+ }));
93
+ }
94
+ if (uri === NS.fn && arity === 0 && resolved.focus) {
95
+ if (local === "position") {
96
+ return (ctx) => (contextItem(ctx), [integerItem(ctx.position)]);
97
+ }
98
+ if (local === "last") {
99
+ return (ctx) => (contextItem(ctx), [integerItem(ctx.size)]);
100
+ }
101
+ }
102
+ const { call } = resolved;
103
+ if (arity === 1) {
104
+ const [arg] = args;
105
+ return (ctx) => call([arg(ctx)], ctx);
106
+ }
107
+ return (ctx) =>
108
+ call(
109
+ args.map((arg) => arg(ctx)),
110
+ ctx,
111
+ );
112
+ },
113
+
114
+ NamedFunctionRef(node, scope) {
115
+ const { sc } = scope;
116
+ const uri = namespaceOf(node.name, sc, sc.defaultFunctionNamespace);
117
+ const resolved = resolveFunction(uri, node.name.local, node.arity, sc);
118
+ return (ctx) => [functionItemOf(resolved, ctx)];
119
+ },
120
+
121
+ DynamicFunctionCall(node, scope, compile) {
122
+ const target = compile(node.functionExpr, scope);
123
+ const arity = node.arguments.length;
124
+ const args = node.arguments.map((arg) =>
125
+ isPlaceholder(arg) ? null : compile(arg, scope),
126
+ );
127
+ if (args.includes(null)) {
128
+ return partial(args, null, (ctx) => {
129
+ const item = calledFunction(target(ctx), arity);
130
+ return {
131
+ invoke: (values) => item.invoke(values),
132
+ signature: signatureOf(item),
133
+ };
134
+ });
135
+ }
136
+ return (ctx) => {
137
+ const item = calledFunction(target(ctx), arity);
138
+ return item.invoke(args.map((arg) => arg(ctx)));
139
+ };
140
+ },
141
+
142
+ ArrowExpr(node, scope, compile) {
143
+ const args = [node.expr, ...node.arguments];
144
+ const { start, end } = node;
145
+ if (node.functionName) {
146
+ return compile(
147
+ {
148
+ type: "FunctionCall",
149
+ name: node.functionName,
150
+ arguments: args,
151
+ start,
152
+ end,
153
+ },
154
+ scope,
155
+ );
156
+ }
157
+ return compile(
158
+ {
159
+ type: "DynamicFunctionCall",
160
+ functionExpr: node.functionExpr,
161
+ arguments: args,
162
+ start,
163
+ end,
164
+ },
165
+ scope,
166
+ );
167
+ },
168
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The coercion rules (XPath 3.1 section 3.1.5.2, "function conversion
3
+ * rules"): how a value is converted to an expected sequence type when it
4
+ * is passed to a function or returned from one. Atomization, casting of
5
+ * xs:untypedAtomic, numeric and URI promotion, function coercion, then a
6
+ * type check (XPTY0004); in XPath 1.0 compatibility mode the 1.0
7
+ * conversions to string and number come first.
8
+ *
9
+ * @module @tradik/xslt3/xpath/eval/coercion
10
+ */
11
+
12
+ import { XPathError } from "../../errors.js";
13
+ import { isFunctionItem } from "../../xdm/atomic.js";
14
+ import { cast } from "../../xdm/cast.js";
15
+ import { toNumber } from "../../xdm/generalCompare.js";
16
+ import { atomize, stringValue } from "../../xdm/nodes.js";
17
+ import { derivesFrom, types } from "../../xdm/types.js";
18
+ import { FunctionItem } from "../../items/function.js";
19
+ import { stringItem } from "./atomics.js";
20
+ import { matchesSequenceType } from "./sequenceType.js";
21
+
22
+ /**
23
+ * Promotes or casts one atomic value towards the expected atomic types.
24
+ * @param {import("../../xdm/atomic.js").AtomicValue} value
25
+ * @param {object[]} expected - Atomic type descriptors
26
+ * @returns {import("../../xdm/atomic.js").AtomicValue}
27
+ */
28
+ function convertAtomic(value, expected) {
29
+ if (expected.some((t) => derivesFrom(value.type, t))) return value;
30
+ if (value.type === types.untypedAtomic) {
31
+ if (
32
+ expected.some((t) => t.primitive === types.QName || t === types.NOTATION)
33
+ ) {
34
+ throw new XPathError(
35
+ "XPTY0117",
36
+ "An untyped value cannot be converted to xs:QName or xs:NOTATION",
37
+ );
38
+ }
39
+ const target = expected.length > 1 ? types.double : expected[0];
40
+ return target.abstract ? value : cast(value, target);
41
+ }
42
+ const primitive = value.type.primitive.localName;
43
+ for (const target of expected) {
44
+ const name = target.localName;
45
+ const numeric =
46
+ (name === "double" &&
47
+ (primitive === "decimal" || primitive === "float")) ||
48
+ (name === "float" && primitive === "decimal");
49
+ if (numeric || (name === "string" && primitive === "anyURI")) {
50
+ return cast(value, target);
51
+ }
52
+ }
53
+ return value;
54
+ }
55
+
56
+ /**
57
+ * XPath 1.0 compatibility mode conversions of an argument: only the
58
+ * first item is kept for a single expected item, converted with fn:string
59
+ * for xs:string and fn:number for xs:double.
60
+ * @param {Array} sequence
61
+ * @param {object|null} itemType - Expected item type
62
+ * @param {string} occurrence
63
+ * @returns {Array}
64
+ */
65
+ function compatible(sequence, itemType, occurrence) {
66
+ if (occurrence === "*" || occurrence === "+") return sequence;
67
+ const first = sequence.slice(0, 1);
68
+ const expected = itemType?.kind === "atomic" ? itemType.types[0] : null;
69
+ if (expected === types.string) {
70
+ return [stringItem(first.length ? stringValue(first[0]) : "")];
71
+ }
72
+ if (expected === types.double) {
73
+ const value = atomize(first)[0];
74
+ return [value ? toNumber(value) : toNumber(stringItem("NaN"))];
75
+ }
76
+ return first;
77
+ }
78
+
79
+ /**
80
+ * Function coercion: wraps a function item so that it has the signature
81
+ * of a function test.
82
+ * @param {*} item
83
+ * @param {object} test - Compiled TypedFunctionTest
84
+ * @param {string} what - Description for error messages
85
+ * @returns {FunctionItem}
86
+ */
87
+ function coerceFunction(item, test, what) {
88
+ if (item.arity !== test.params.length) {
89
+ throw new XPathError(
90
+ "XPTY0004",
91
+ `The ${what} must be a function of arity ${test.params.length}`,
92
+ );
93
+ }
94
+ return new FunctionItem({
95
+ name: item.name,
96
+ arity: item.arity,
97
+ signature: { params: test.params, returns: test.returns },
98
+ invoke: (args) =>
99
+ coerce(
100
+ item.invoke(
101
+ args.map((arg, i) =>
102
+ coerce(arg, test.params[i], {
103
+ what: `argument ${i + 1} of ${what}`,
104
+ }),
105
+ ),
106
+ ),
107
+ test.returns,
108
+ { what: `result of ${what}` },
109
+ ),
110
+ });
111
+ }
112
+
113
+ /**
114
+ * Converts a value to an expected sequence type.
115
+ * @param {Array} sequence
116
+ * @param {import("./sequenceType.js").SequenceType} type
117
+ * @param {{what: string, compatible?: boolean}} options - `what` names
118
+ * the value in error messages; `compatible` applies the XPath 1.0 rules
119
+ * @returns {Array} the converted value
120
+ * @throws {XPathError} XPTY0004 when the value does not match the type,
121
+ * cast errors (FORG0001...) for untyped values
122
+ */
123
+ export function coerce(sequence, type, options) {
124
+ const itemType = type.itemType;
125
+ if (itemType?.kind === "item" && type.occurrence === "*") return sequence;
126
+ let value = sequence;
127
+ if (options.compatible) {
128
+ value = compatible(value, itemType, type.occurrence);
129
+ }
130
+ if (itemType?.kind === "atomic") {
131
+ value = atomize(value).map((v) => convertAtomic(v, itemType.types));
132
+ } else if (itemType?.kind === "function") {
133
+ value = value.map((item) =>
134
+ isFunctionItem(item) && !itemType.matches(item)
135
+ ? coerceFunction(item, itemType, options.what)
136
+ : item,
137
+ );
138
+ }
139
+ if (!matchesSequenceType(value, type)) {
140
+ throw new XPathError(
141
+ "XPTY0004",
142
+ `The ${options.what} does not match the required type`,
143
+ );
144
+ }
145
+ return value;
146
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * The compiler: static analysis and translation of an AST into nested
3
+ * closures, in one pass. Names are resolved (XPST0081), variables
4
+ * (XPST0008), functions (XPST0017) and types (XPST0051, XPST0080) checked,
5
+ * and each node becomes an evaluator `(ctx) => Item[]`, so evaluation
6
+ * never walks the AST again.
7
+ *
8
+ * @module @tradik/xslt3/xpath/eval/compiler
9
+ */
10
+
11
+ import { XPathError } from "../../errors.js";
12
+ import { callCompilers } from "./calls.js";
13
+ import { constructorCompilers } from "./constructors.js";
14
+ import { flowCompilers } from "./flow.js";
15
+ import { inlineCompilers } from "./inline.js";
16
+ import { operatorCompilers } from "./operators.js";
17
+ import { pathCompilers } from "./paths.js";
18
+ import { primaryCompilers } from "./primary.js";
19
+ import { typeCompilers } from "./typeExprs.js";
20
+
21
+ /** Compilers by AST node type. */
22
+ const compilers = {
23
+ ...primaryCompilers,
24
+ ...flowCompilers,
25
+ ...operatorCompilers,
26
+ ...typeCompilers,
27
+ ...pathCompilers,
28
+ ...callCompilers,
29
+ ...inlineCompilers,
30
+ ...constructorCompilers,
31
+ };
32
+
33
+ /**
34
+ * Compiles an expression node.
35
+ * @param {import("../syntax/ast.js").Expr} node
36
+ * @param {import("./scope.js").Scope} scope
37
+ * @returns {import("./scope.js").Evaluator}
38
+ * @throws {XPathError} the static errors of the expression
39
+ */
40
+ export function compileNode(node, scope) {
41
+ const compiler = compilers[node.type];
42
+ if (!compiler) {
43
+ throw new XPathError("XPST0003", `Unsupported expression ${node.type}`);
44
+ }
45
+ return compiler(node, scope, compileNode);
46
+ }
47
+
48
+ /**
49
+ * Runs a function, turning the RangeErrors of JavaScript limits (stack
50
+ * depth of deeply nested expressions or recursive functions, string,
51
+ * array and BigInt sizes) into XPDY0130, the error for implementation
52
+ * limits.
53
+ * @template T
54
+ * @param {() => T} run
55
+ * @returns {T}
56
+ * @throws {XPathError} XPDY0130 instead of a RangeError
57
+ */
58
+ export function withinLimits(run) {
59
+ try {
60
+ return run();
61
+ } catch (error) {
62
+ if (error instanceof RangeError) {
63
+ throw new XPathError(
64
+ "XPDY0130",
65
+ `Implementation limit exceeded: ${error.message}`,
66
+ { cause: error },
67
+ );
68
+ }
69
+ throw error;
70
+ }
71
+ }
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Compilers of map and array constructors and of the lookup operator `?`
3
+ * (XPath 3.1 sections 3.11.1 to 3.11.3).
4
+ *
5
+ * @module @tradik/xslt3/xpath/eval/constructors
6
+ */
7
+
8
+ import { XPathError } from "../../errors.js";
9
+ import { XdmArray } from "../../items/array.js";
10
+ import { XdmMap } from "../../items/map.js";
11
+ import { AtomicValue, isArray, isMap } from "../../xdm/atomic.js";
12
+ import { atomize } from "../../xdm/nodes.js";
13
+ import { types } from "../../xdm/types.js";
14
+ import { contextItem } from "./scope.js";
15
+
16
+ /**
17
+ * Compiles the key specifier of a lookup.
18
+ * @param {import("../syntax/ast.js").Lookup} node
19
+ * @param {import("./scope.js").Scope} scope
20
+ * @param {Function} compile
21
+ * @returns {((ctx: object) => import("../../xdm/atomic.js").AtomicValue[])|null}
22
+ * the keys, null for the wildcard
23
+ */
24
+ function compileKeys(node, scope, compile) {
25
+ switch (node.keyKind) {
26
+ case "wildcard":
27
+ return null;
28
+ case "name": {
29
+ const keys = [new AtomicValue(types.string, node.key)];
30
+ return () => keys;
31
+ }
32
+ case "integer": {
33
+ const keys = [new AtomicValue(types.integer, BigInt(node.key))];
34
+ return () => keys;
35
+ }
36
+ default: {
37
+ const expr = compile(node.key, scope);
38
+ return (ctx) => atomize(expr(ctx));
39
+ }
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Looks keys up in one map or array.
45
+ * @param {*} item
46
+ * @param {import("../../xdm/atomic.js").AtomicValue[]|null} keys - null for
47
+ * all entries or members
48
+ * @param {Array} result - Values are appended
49
+ * @throws {XPathError} XPTY0004 for an item that is neither a map nor an
50
+ * array, or a non-integer array key; FOAY0001 for a position out of
51
+ * bounds
52
+ */
53
+ function lookup(item, keys, result) {
54
+ if (isMap(item)) {
55
+ if (keys === null) {
56
+ for (const { value } of item.entries.values()) result.push(...value);
57
+ } else {
58
+ for (const key of keys) result.push(...(item.get(key) ?? []));
59
+ }
60
+ } else if (isArray(item)) {
61
+ if (keys === null) {
62
+ for (const member of item.members) result.push(...member);
63
+ } else {
64
+ for (const key of keys) result.push(...item.invoke([[key]]));
65
+ }
66
+ } else {
67
+ throw new XPathError(
68
+ "XPTY0004",
69
+ "The lookup operator applies to maps and arrays only",
70
+ );
71
+ }
72
+ }
73
+
74
+ /** Compilers by node type. */
75
+ export const constructorCompilers = {
76
+ MapConstructor(node, scope, compile) {
77
+ const entries = node.entries.map((entry) => ({
78
+ key: compile(entry.key, scope),
79
+ value: compile(entry.value, scope),
80
+ }));
81
+ return (ctx) => {
82
+ const pairs = entries.map(({ key, value }) => {
83
+ const keys = atomize(key(ctx));
84
+ if (keys.length !== 1) {
85
+ throw new XPathError(
86
+ "XPTY0004",
87
+ "A map key must be a single atomic value",
88
+ );
89
+ }
90
+ return [keys[0], value(ctx)];
91
+ });
92
+ const duplicate = () => {
93
+ throw new XPathError("XQDY0137", "Duplicate key in a map constructor");
94
+ };
95
+ return [XdmMap.from(pairs, duplicate)];
96
+ };
97
+ },
98
+
99
+ SquareArrayConstructor(node, scope, compile) {
100
+ const members = node.members.map((member) => compile(member, scope));
101
+ return (ctx) => [new XdmArray(members.map((member) => member(ctx)))];
102
+ },
103
+
104
+ CurlyArrayConstructor(node, scope, compile) {
105
+ const expr = compile(node.expr, scope);
106
+ return (ctx) => [new XdmArray(expr(ctx).map((item) => [item]))];
107
+ },
108
+
109
+ Lookup(node, scope, compile) {
110
+ const base = compile(node.base, scope);
111
+ const keys = compileKeys(node, scope, compile);
112
+ return (ctx) => {
113
+ const items = base(ctx);
114
+ const keyValues = keys && keys(ctx);
115
+ const result = [];
116
+ for (const item of items) lookup(item, keyValues, result);
117
+ return result;
118
+ };
119
+ },
120
+
121
+ UnaryLookup(node, scope, compile) {
122
+ const keys = compileKeys(node, scope, compile);
123
+ return (ctx) => {
124
+ const result = [];
125
+ lookup(contextItem(ctx), keys && keys(ctx), result);
126
+ return result;
127
+ };
128
+ },
129
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Decimal formats of the static context (XPath 3.1 section 2.1.1), used by
3
+ * fn:format-number. Formats are given by name: a lexical QName or an EQName
4
+ * (prefixes resolved with the static namespaces), "" for the default one.
5
+ *
6
+ * Properties are read in camelCase (`decimalSeparator`) or as the
7
+ * attribute names of xsl:decimal-format (`decimal-separator`, `NaN`).
8
+ *
9
+ * @module @tradik/xslt3/xpath/eval/decimalFormats
10
+ */
11
+
12
+ /** Property names of a decimal format, by attribute name. */
13
+ const PROPERTIES = {
14
+ "decimal-separator": "decimalSeparator",
15
+ "grouping-separator": "groupingSeparator",
16
+ "exponent-separator": "exponentSeparator",
17
+ infinity: "infinity",
18
+ "minus-sign": "minusSign",
19
+ NaN: "nan",
20
+ nan: "nan",
21
+ percent: "percent",
22
+ "per-mille": "perMille",
23
+ "zero-digit": "zeroDigit",
24
+ digit: "digit",
25
+ "pattern-separator": "patternSeparator",
26
+ };
27
+ const CAMEL = new Set(Object.values(PROPERTIES));
28
+
29
+ /**
30
+ * The properties of a format definition.
31
+ * @param {object} definition
32
+ * @returns {object} camelCase properties
33
+ */
34
+ function propertiesOf(definition) {
35
+ const properties = {};
36
+ for (const [key, value] of Object.entries(definition)) {
37
+ const name = PROPERTIES[key] ?? (CAMEL.has(key) ? key : null);
38
+ if (name) properties[name] = value;
39
+ }
40
+ return properties;
41
+ }
42
+
43
+ /**
44
+ * Expanded name of a format definition: a prefix declared on the
45
+ * definition itself (an `xmlns:p` property, as xsl:decimal-format
46
+ * elements have) comes first, then the static namespaces.
47
+ * @param {string} name
48
+ * @param {object} definition
49
+ * @param {(name: string) => string} expand
50
+ * @returns {string|null} Clark name, "" for the default format, null when
51
+ * the prefix is not declared
52
+ */
53
+ function definitionKey(name, definition, expand) {
54
+ if (name === "") return "";
55
+ const colon = name.indexOf(":");
56
+ const own =
57
+ colon > 0 && !name.startsWith("Q{")
58
+ ? definition[`xmlns:${name.slice(0, colon)}`]
59
+ : undefined;
60
+ if (own !== undefined) return `{${own}}${name.slice(colon + 1)}`;
61
+ try {
62
+ return expand(name);
63
+ } catch {
64
+ return null;
65
+ }
66
+ }
67
+
68
+ /**
69
+ * Builds the decimal formats of a static context.
70
+ * @param {Array<object>|object|Map|undefined} formats - An array of
71
+ * definitions with a `name` property (absent for the default format),
72
+ * or definitions by name
73
+ * @param {(name: string) => string} expand - Expands a name as written
74
+ * to a Clark name (throws for an undeclared prefix)
75
+ * @returns {{get: (name: string) => object|undefined}} the formats by
76
+ * name (surrounding whitespace ignored); get() returns undefined for an
77
+ * unknown name or prefix
78
+ */
79
+ export function createDecimalFormats(formats, expand) {
80
+ const entries = Array.isArray(formats)
81
+ ? formats.map((format) => [format.name ?? "", format])
82
+ : formats instanceof Map
83
+ ? [...formats]
84
+ : Object.entries(formats ?? {});
85
+ const byName = new Map();
86
+ for (const [name, format] of entries) {
87
+ const key = definitionKey(name.trim(), format, expand);
88
+ if (key !== null) byName.set(key, propertiesOf(format));
89
+ }
90
+ return {
91
+ get: (name) => byName.get(definitionKey(name.trim(), {}, expand)),
92
+ };
93
+ }
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Axis results remembered for the rest of an evaluation, so that
3
+ * `for $c in ... return //item[@cat = $c]` walks the tree and looks the
4
+ * attributes up once instead of once per iteration:
5
+ *
6
+ * - the descendant axes of a document node, always;
7
+ * - the attributes looked up by name, once the evaluation has walked a
8
+ * tree a second time (a remembered descendant axis was used): before
9
+ * that, in one pass over the tree, remembering costs more than it saves.
10
+ *
11
+ * Only an evaluation that owns its dynamic context turns it on
12
+ * (compileXPath's evaluate sets `dyn.descendantMemo`): an XPath evaluation
13
+ * does not change existing trees (the nodes that functions build are new,
14
+ * complete trees), so a remembered result stays exact while it runs. A
15
+ * caller that shares a dynamic context over trees it changes leaves it
16
+ * off.
17
+ *
18
+ * @module @tradik/xslt3/xpath/eval/descendantMemo
19
+ */
20
+
21
+ import { axes } from "./axes.js";
22
+
23
+ /** Axes worth remembering: whole subtrees. */
24
+ const MEMO_AXES = new Set(["descendant", "descendant-or-self"]);
25
+
26
+ /**
27
+ * @typedef {object} DescendantMemo
28
+ * @property {Map<string, Map<Function, Map<Node, Node[]>>>|null} results -
29
+ * By axis, node test and context node
30
+ * @property {boolean} repeated - Whether a remembered descendant axis was
31
+ * used again
32
+ */
33
+
34
+ /** @returns {DescendantMemo} the memo of an evaluation */
35
+ export const createDescendantMemo = () => ({ results: null, repeated: false });
36
+
37
+ /**
38
+ * Whether the result of an axis is remembered.
39
+ * @returns {boolean}
40
+ */
41
+ function isRemembered(memo, name, node, test, limit) {
42
+ if (name === "attribute") {
43
+ return memo.repeated && test.qname !== undefined && limit >= 1;
44
+ }
45
+ const type = node.nodeType;
46
+ return (
47
+ limit === Infinity && MEMO_AXES.has(name) && (type === 9 || type === 11)
48
+ );
49
+ }
50
+
51
+ /**
52
+ * The nodes of an axis, from the memo of the evaluation when there is one
53
+ * and the axis is remembered.
54
+ * @param {string} name - Axis name
55
+ * @param {Node} node - Context node
56
+ * @param {(node: Node) => boolean} test - Node test (one function per
57
+ * compiled step, so it identifies the step)
58
+ * @param {number} limit - Most nodes to collect
59
+ * @param {object} dyn - Dynamic context
60
+ * @returns {Node[]} the nodes in axis order (a new array)
61
+ */
62
+ export function axisNodes(name, node, test, limit, dyn) {
63
+ const memo = dyn.descendantMemo;
64
+ if (!memo || !isRemembered(memo, name, node, test, limit)) {
65
+ return axes[name](node, test, limit);
66
+ }
67
+ // Created on first use: most evaluations never need it
68
+ memo.results ??= new Map();
69
+ let byTest = memo.results.get(name);
70
+ if (!byTest) memo.results.set(name, (byTest = new Map()));
71
+ let byNode = byTest.get(test);
72
+ if (!byNode) byTest.set(test, (byNode = new Map()));
73
+ let nodes = byNode.get(node);
74
+ if (nodes) memo.repeated = true;
75
+ else byNode.set(node, (nodes = axes[name](node, test, limit)));
76
+ return nodes.slice();
77
+ }