@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,190 @@
1
+ /**
2
+ * Compilers of the operator expressions: and/or, comparisons, `||`, `to`,
3
+ * arithmetic and unary plus/minus (XPath 3.1 sections 3.4 to 3.8), with
4
+ * the XPath 1.0 compatibility mode rules for arithmetic.
5
+ *
6
+ * @module @tradik/xslt3/xpath/eval/operators
7
+ */
8
+
9
+ import { XPathError } from "../../errors.js";
10
+ import { arithmetic, unaryArithmetic } from "../../xdm/arithmetic.js";
11
+ import { AtomicValue, isNode } from "../../xdm/atomic.js";
12
+ import { cast } from "../../xdm/cast.js";
13
+ import { valueCompare } from "../../xdm/compare.js";
14
+ import { generalCompare, toNumber } from "../../xdm/generalCompare.js";
15
+ import { canonicalString } from "../../xdm/lexical.js";
16
+ import { atomize, effectiveBooleanValue } from "../../xdm/nodes.js";
17
+ import { derivesFrom, isNumericType, types } from "../../xdm/types.js";
18
+ import { atomizeOptional, booleanItem, stringItem } from "./atomics.js";
19
+
20
+ /** Largest sequence a range expression may build. */
21
+ export const MAX_RANGE = 2 ** 25;
22
+
23
+ const EMPTY = Object.freeze([]);
24
+ const NAN = new AtomicValue(types.double, NaN);
25
+
26
+ /**
27
+ * Operand of an arithmetic operator in XPath 1.0 compatibility mode: the
28
+ * first atomized item, as xs:double unless it is a duration or date/time.
29
+ * @param {Array} sequence
30
+ * @returns {AtomicValue}
31
+ */
32
+ function compatibleOperand(sequence) {
33
+ const first = atomize(sequence.slice(0, 1))[0];
34
+ if (first === undefined) return NAN;
35
+ const primitive = first.type.primitive.localName;
36
+ const toDouble =
37
+ isNumericType(first.type) ||
38
+ ["boolean", "string", "untypedAtomic", "anyURI"].includes(primitive);
39
+ return toDouble ? toNumber(first) : first;
40
+ }
41
+
42
+ /**
43
+ * @param {Array} sequence
44
+ * @returns {*} the single node of a node comparison operand, undefined
45
+ * when empty
46
+ * @throws {XPathError} XPTY0004 when the operand is not one node
47
+ */
48
+ function nodeOperand(sequence) {
49
+ if (sequence.length === 0) return undefined;
50
+ if (sequence.length > 1 || !isNode(sequence[0])) {
51
+ throw new XPathError(
52
+ "XPTY0004",
53
+ "A node comparison operand must be a single node",
54
+ );
55
+ }
56
+ return sequence[0];
57
+ }
58
+
59
+ /**
60
+ * Operand of a value comparison, xs:untypedAtomic cast to xs:string.
61
+ * @param {Array} sequence
62
+ * @returns {AtomicValue|undefined}
63
+ */
64
+ function valueOperand(sequence) {
65
+ const value = atomizeOptional(sequence, "operand of a value comparison");
66
+ return value?.type === types.untypedAtomic
67
+ ? cast(value, types.string)
68
+ : value;
69
+ }
70
+
71
+ /**
72
+ * Operand of a range expression.
73
+ * @param {Array} sequence
74
+ * @returns {bigint|undefined}
75
+ */
76
+ function rangeOperand(sequence) {
77
+ let value = atomizeOptional(sequence, "operand of to");
78
+ if (value === undefined) return undefined;
79
+ if (value.type === types.untypedAtomic) value = cast(value, types.integer);
80
+ if (!derivesFrom(value.type, types.integer)) {
81
+ throw new XPathError("XPTY0004", "The operands of to must be integers");
82
+ }
83
+ return value.value;
84
+ }
85
+
86
+ /** Comparison compilers by kind. */
87
+ const comparisons = {
88
+ general: (op, left, right, compatible) => (ctx) => {
89
+ const options = compatible
90
+ ? ctx.dyn.compatibleCompareOptions
91
+ : ctx.dyn.compareOptions;
92
+ return [booleanItem(generalCompare(left(ctx), op, right(ctx), options))];
93
+ },
94
+ value: (op, left, right) => (ctx) => {
95
+ const a = valueOperand(left(ctx));
96
+ const b = valueOperand(right(ctx));
97
+ if (a === undefined || b === undefined) return EMPTY;
98
+ return [booleanItem(valueCompare(a, op, b, ctx.dyn.compareOptions))];
99
+ },
100
+ node: (op, left, right) => (ctx) => {
101
+ const a = nodeOperand(left(ctx));
102
+ const b = nodeOperand(right(ctx));
103
+ if (a === undefined || b === undefined) return EMPTY;
104
+ if (op === "is") return [booleanItem(a === b)];
105
+ const order = ctx.dyn.order.compare(a, b);
106
+ return [booleanItem(op === "<<" ? order < 0 : order > 0)];
107
+ },
108
+ };
109
+
110
+ /** Compilers by node type. */
111
+ export const operatorCompilers = {
112
+ LogicalExpr(node, scope, compile) {
113
+ const left = compile(node.left, scope);
114
+ const right = compile(node.right, scope);
115
+ const isOr = node.operator === "or";
116
+ return (ctx) => {
117
+ const first = effectiveBooleanValue(left(ctx));
118
+ if (first === isOr) return [booleanItem(isOr)];
119
+ return [booleanItem(effectiveBooleanValue(right(ctx)))];
120
+ };
121
+ },
122
+
123
+ ComparisonExpr(node, scope, compile) {
124
+ return comparisons[node.kind](
125
+ node.operator,
126
+ compile(node.left, scope),
127
+ compile(node.right, scope),
128
+ scope.sc.backwardsCompatible,
129
+ );
130
+ },
131
+
132
+ StringConcatExpr(node, scope, compile) {
133
+ const left = compile(node.left, scope);
134
+ const right = compile(node.right, scope);
135
+ const text = (sequence) => {
136
+ const value = atomizeOptional(sequence, "operand of ||");
137
+ return value === undefined ? "" : canonicalString(value);
138
+ };
139
+ return (ctx) => [stringItem(text(left(ctx)) + text(right(ctx)))];
140
+ },
141
+
142
+ RangeExpr(node, scope, compile) {
143
+ const left = compile(node.left, scope);
144
+ const right = compile(node.right, scope);
145
+ return (ctx) => {
146
+ const low = rangeOperand(left(ctx));
147
+ const high = rangeOperand(right(ctx));
148
+ if (low === undefined || high === undefined || low > high) return EMPTY;
149
+ if (high - low >= BigInt(MAX_RANGE)) {
150
+ throw new XPathError("XPDY0130", "The range is too large");
151
+ }
152
+ const result = [];
153
+ for (let i = low; i <= high; i++) {
154
+ result.push(new AtomicValue(types.integer, i));
155
+ }
156
+ return result;
157
+ };
158
+ },
159
+
160
+ ArithmeticExpr(node, scope, compile) {
161
+ const left = compile(node.left, scope);
162
+ const right = compile(node.right, scope);
163
+ const op = node.operator;
164
+ if (scope.sc.backwardsCompatible) {
165
+ return (ctx) => {
166
+ const a = compatibleOperand(left(ctx));
167
+ const b = compatibleOperand(right(ctx));
168
+ return [arithmetic(a, op, b, ctx.dyn.compareOptions)];
169
+ };
170
+ }
171
+ return (ctx) => {
172
+ const a = atomizeOptional(left(ctx), `operand of ${op}`);
173
+ const b = atomizeOptional(right(ctx), `operand of ${op}`);
174
+ if (a === undefined || b === undefined) return EMPTY;
175
+ return [arithmetic(a, op, b, ctx.dyn.compareOptions)];
176
+ };
177
+ },
178
+
179
+ UnaryExpr(node, scope, compile) {
180
+ const operand = compile(node.operand, scope);
181
+ const op = node.operator;
182
+ if (scope.sc.backwardsCompatible) {
183
+ return (ctx) => [unaryArithmetic(op, compatibleOperand(operand(ctx)))];
184
+ }
185
+ return (ctx) => {
186
+ const a = atomizeOptional(operand(ctx), `operand of unary ${op}`);
187
+ return a === undefined ? EMPTY : [unaryArithmetic(op, a)];
188
+ };
189
+ },
190
+ };
@@ -0,0 +1,163 @@
1
+ /**
2
+ * Compilers of path expressions, axis steps, predicates, filter
3
+ * expressions and the node set operators (XPath 3.1 sections 3.3, 3.4).
4
+ *
5
+ * `//T` is compiled as `descendant::T` when the predicates of T do not
6
+ * depend on the context position (see positional.js), instead of
7
+ * `descendant-or-self::node()/child::T`, which gives the same nodes.
8
+ *
9
+ * @module @tradik/xslt3/xpath/eval/paths
10
+ */
11
+
12
+ import { XPathError } from "../../errors.js";
13
+ import { isNode } from "../../xdm/atomic.js";
14
+ import { REVERSE_AXES } from "./axes.js";
15
+ import { axisNodes } from "./descendantMemo.js";
16
+ import { rootOf } from "./domNodes.js";
17
+ import {
18
+ compileGroupedChildren,
19
+ isGroupedChildren,
20
+ } from "./groupedChildren.js";
21
+ import { compileNodeTest } from "./nodeTests.js";
22
+ import { isPositionIndependent } from "./positional.js";
23
+ import { compilePredicate } from "./predicates.js";
24
+ import { contextItem } from "./scope.js";
25
+ import { stepper } from "./steps.js";
26
+
27
+ /**
28
+ * @param {import("./scope.js").Context} ctx
29
+ * @param {string} code - Error code when the context item is not a node
30
+ * @returns {Node} the context node
31
+ */
32
+ function contextNode(ctx, code) {
33
+ const item = contextItem(ctx);
34
+ if (!isNode(item)) {
35
+ throw new XPathError(code, "The context item is not a node");
36
+ }
37
+ return item;
38
+ }
39
+
40
+ /**
41
+ * Whether a step is `descendant-or-self::node()` and the next one a child
42
+ * step whose predicates do not depend on the position, which together are
43
+ * a descendant step.
44
+ */
45
+ const isDescendantShortcut = (step, next, sc) =>
46
+ step.type === "AxisStep" &&
47
+ step.axis === "descendant-or-self" &&
48
+ step.nodeTest.type === "AnyKindTest" &&
49
+ step.predicates.length === 0 &&
50
+ next?.type === "AxisStep" &&
51
+ next.axis === "child" &&
52
+ next.predicates.every((p) => isPositionIndependent(p, sc));
53
+
54
+ /**
55
+ * How many nodes of an axis a step needs: N for a first predicate that is
56
+ * the integer literal N, all of them otherwise.
57
+ * @param {import("../syntax/ast.js").Expr|undefined} predicate
58
+ * @returns {number}
59
+ */
60
+ const positionalLimit = (predicate) =>
61
+ predicate?.type === "NumericLiteral" && predicate.kind === "integer"
62
+ ? Math.max(Number(predicate.value), 0)
63
+ : Infinity;
64
+
65
+ /** Marks a `descendant-or-self::node()/child::T[P]` pair in a path. */
66
+ const GROUPED = "GroupedChildren";
67
+
68
+ /** Compilers by node type. */
69
+ export const pathCompilers = {
70
+ AxisStep(node, scope, compile) {
71
+ const axis = node.axis;
72
+ const test = compileNodeTest(node.nodeTest, node.axis, scope.sc);
73
+ const predicates = node.predicates.map((p) =>
74
+ compilePredicate(p, scope, compile),
75
+ );
76
+ const limit = positionalLimit(node.predicates[0]);
77
+ const reverse = REVERSE_AXES.has(node.axis);
78
+ return (ctx) => {
79
+ const item = contextNode(ctx, "XPTY0020");
80
+ let nodes = axisNodes(axis, item, test, limit, ctx.dyn);
81
+ for (const predicate of predicates) nodes = predicate(nodes, ctx);
82
+ return reverse ? nodes.reverse() : nodes;
83
+ };
84
+ },
85
+
86
+ PathExpr(node, scope, compile) {
87
+ const steps = [...node.steps];
88
+ for (let i = 0; i < steps.length - 1; i++) {
89
+ if (isDescendantShortcut(steps[i], steps[i + 1], scope.sc)) {
90
+ steps.splice(i, 2, { ...steps[i + 1], axis: "descendant" });
91
+ }
92
+ }
93
+ for (let i = 0; i < steps.length - 1; i++) {
94
+ if (isGroupedChildren(steps[i], steps[i + 1])) {
95
+ steps.splice(i, 2, { type: GROUPED, step: steps[i + 1] });
96
+ }
97
+ }
98
+ const compiled = steps.map((step) => {
99
+ if (step.type === GROUPED) {
100
+ const run = compileGroupedChildren(step.step, scope, compile);
101
+ const evaluate = (ctx) => run([contextNode(ctx, "XPTY0020")], ctx);
102
+ return { evaluate, run };
103
+ }
104
+ const evaluate = compile(step, scope);
105
+ const axis = step.type === "AxisStep" ? step.axis : null;
106
+ return { evaluate, run: stepper(evaluate, axis) };
107
+ });
108
+ const sortedStart =
109
+ node.absolute ||
110
+ steps[0].type === "AxisStep" ||
111
+ steps[0].type === GROUPED;
112
+ const start = node.absolute
113
+ ? (ctx) => {
114
+ const root = rootOf(contextNode(ctx, "XPTY0020"));
115
+ if (root.nodeType !== 9 && root.nodeType !== 11) {
116
+ throw new XPathError(
117
+ "XPDY0050",
118
+ "The root of the context node is not a document node",
119
+ );
120
+ }
121
+ return [root];
122
+ }
123
+ : compiled.shift().evaluate;
124
+ return (ctx) => {
125
+ let current = start(ctx);
126
+ let sorted = sortedStart;
127
+ for (const { run } of compiled) {
128
+ current = run(current, ctx, sorted);
129
+ sorted = true;
130
+ }
131
+ return current;
132
+ };
133
+ },
134
+
135
+ FilterExpr(node, scope, compile) {
136
+ const base = compile(node.base, scope);
137
+ const predicate = compilePredicate(node.predicate, scope, compile);
138
+ return (ctx) => predicate(base(ctx), ctx);
139
+ },
140
+
141
+ SetExpr(node, scope, compile) {
142
+ const left = compile(node.left, scope);
143
+ const right = compile(node.right, scope);
144
+ const operator = node.operator;
145
+ const nodesOf = (sequence) => {
146
+ if (!sequence.every(isNode)) {
147
+ throw new XPathError(
148
+ "XPTY0004",
149
+ `The operands of ${operator} must be nodes`,
150
+ );
151
+ }
152
+ return sequence;
153
+ };
154
+ return (ctx) => {
155
+ const a = nodesOf(left(ctx));
156
+ const b = nodesOf(right(ctx));
157
+ if (operator === "union") return ctx.dyn.order.sort(a.concat(b));
158
+ const inRight = new Set(b);
159
+ const keep = operator === "intersect";
160
+ return ctx.dyn.order.sort(a.filter((n) => inRight.has(n) === keep));
161
+ };
162
+ },
163
+ };
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Static test of whether a predicate depends on the context position or
3
+ * size: when it does not, `//T[P]` (`descendant-or-self::node()/child::T[P]`)
4
+ * selects the same nodes as `descendant::T[P]`, which needs no sorting.
5
+ *
6
+ * The test is syntactic and conservative: the predicate must evaluate to a
7
+ * boolean or to nodes (never to a number, which would select by position)
8
+ * and must not call position() or last() anywhere.
9
+ *
10
+ * @module @tradik/xslt3/xpath/eval/positional
11
+ */
12
+
13
+ import { NS } from "../../functions/signatures.js";
14
+
15
+ /** Expressions that always return a boolean or nodes. */
16
+ const NON_NUMERIC = new Set([
17
+ "ComparisonExpr",
18
+ "LogicalExpr",
19
+ "QuantifiedExpr",
20
+ "InstanceOfExpr",
21
+ "CastableExpr",
22
+ "AxisStep",
23
+ ]);
24
+
25
+ /** Built-in functions returning xs:boolean. */
26
+ const BOOLEAN_FUNCTIONS = new Set([
27
+ "not",
28
+ "boolean",
29
+ "exists",
30
+ "empty",
31
+ "true",
32
+ "false",
33
+ "contains",
34
+ "starts-with",
35
+ "ends-with",
36
+ "matches",
37
+ "deep-equal",
38
+ ]);
39
+
40
+ /**
41
+ * @param {import("../syntax/ast.js").QName} name
42
+ * @param {import("./staticContext.js").StaticContext} sc
43
+ * @returns {boolean} whether the name is a built-in function name
44
+ */
45
+ const isBuiltIn = (name, sc) =>
46
+ name.uri === null &&
47
+ (name.prefix === null
48
+ ? sc.defaultFunctionNamespace === NS.fn
49
+ : sc.namespaces.get(name.prefix) === NS.fn);
50
+
51
+ /**
52
+ * Whether an AST contains a call or reference to position() or last().
53
+ * @param {*} node
54
+ * @returns {boolean}
55
+ */
56
+ function usesFocusSize(node) {
57
+ if (Array.isArray(node)) return node.some(usesFocusSize);
58
+ if (node === null || typeof node !== "object") return false;
59
+ const named =
60
+ node.type === "FunctionCall" || node.type === "NamedFunctionRef";
61
+ if (named && ["position", "last"].includes(node.name.local)) return true;
62
+ return Object.values(node).some(usesFocusSize);
63
+ }
64
+
65
+ /**
66
+ * Whether a predicate selects the same items whatever the context
67
+ * position and size.
68
+ * @param {import("../syntax/ast.js").Expr} predicate
69
+ * @param {import("./staticContext.js").StaticContext} sc
70
+ * @returns {boolean}
71
+ */
72
+ export function isPositionIndependent(predicate, sc) {
73
+ const { type } = predicate;
74
+ const nonNumeric =
75
+ NON_NUMERIC.has(type) ||
76
+ (type === "PathExpr" && predicate.steps.at(-1)?.type === "AxisStep") ||
77
+ (type === "FunctionCall" &&
78
+ BOOLEAN_FUNCTIONS.has(predicate.name.local) &&
79
+ isBuiltIn(predicate.name, sc));
80
+ return nonNumeric && !usesFocusSize(predicate);
81
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Predicates (XPath 3.1 section 3.3.3): a numeric predicate value selects
3
+ * the item at that position, any other value is taken as an effective
4
+ * boolean value. A literal integer predicate is compiled to an index.
5
+ *
6
+ * @module @tradik/xslt3/xpath/eval/predicates
7
+ */
8
+
9
+ import { isAtomic } from "../../xdm/atomic.js";
10
+ import { effectiveBooleanValue } from "../../xdm/nodes.js";
11
+ import { isNumericType } from "../../xdm/types.js";
12
+ import { withFocus } from "./scope.js";
13
+
14
+ /**
15
+ * A compiled predicate: filters a sequence (in its order, which gives the
16
+ * context positions).
17
+ * @typedef {(items: Array, ctx: import("./scope.js").Context) => Array} Predicate
18
+ */
19
+
20
+ /**
21
+ * Whether a predicate value keeps the item at a position.
22
+ * @param {Array} value
23
+ * @param {number} position
24
+ * @returns {boolean}
25
+ */
26
+ export function predicateTruth(value, position) {
27
+ if (value.length === 1 && isAtomic(value[0])) {
28
+ const { type, value: number } = value[0];
29
+ if (isNumericType(type)) return Number(number) === position;
30
+ }
31
+ return effectiveBooleanValue(value);
32
+ }
33
+
34
+ /**
35
+ * @param {import("../syntax/ast.js").Expr} node - Predicate expression
36
+ * @param {import("./scope.js").Scope} scope
37
+ * @param {Function} compile
38
+ * @returns {Predicate}
39
+ */
40
+ export function compilePredicate(node, scope, compile) {
41
+ if (node.type === "NumericLiteral" && node.kind === "integer") {
42
+ const index = Number(node.value) - 1;
43
+ return (items) =>
44
+ index >= 0 && index < items.length ? [items[index]] : [];
45
+ }
46
+ const predicate = compile(node, scope);
47
+ return (items, ctx) => {
48
+ const size = items.length;
49
+ const result = [];
50
+ for (let i = 0; i < size; i++) {
51
+ const value = predicate(withFocus(ctx, items[i], i + 1, size));
52
+ if (predicateTruth(value, i + 1)) result.push(items[i]);
53
+ }
54
+ return result;
55
+ };
56
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Compilers of primary expressions: literals, variable references, the
3
+ * context item, the comma operator and the empty sequence.
4
+ *
5
+ * Evaluators return arrays that callers must not modify: a literal returns
6
+ * the same array every time.
7
+ *
8
+ * @module @tradik/xslt3/xpath/eval/primary
9
+ */
10
+
11
+ import { XPathError } from "../../errors.js";
12
+ import { AtomicValue } from "../../xdm/atomic.js";
13
+ import { Decimal } from "../../xdm/decimal.js";
14
+ import { types } from "../../xdm/types.js";
15
+ import { contextItem, variableDepth } from "./scope.js";
16
+ import { clark, namespaceOf } from "./staticContext.js";
17
+
18
+ const EMPTY = Object.freeze([]);
19
+
20
+ /**
21
+ * @param {import("../syntax/ast.js").NumericLiteral} node
22
+ * @returns {AtomicValue}
23
+ */
24
+ function numericValue(node) {
25
+ if (node.kind === "integer") {
26
+ return new AtomicValue(types.integer, BigInt(node.value));
27
+ }
28
+ if (node.kind === "decimal") {
29
+ return new AtomicValue(types.decimal, Decimal.parse(node.value));
30
+ }
31
+ return new AtomicValue(types.double, Number(node.value));
32
+ }
33
+
34
+ /**
35
+ * Evaluator of a variable reference resolved to a depth.
36
+ * @param {number} depth
37
+ * @param {string} name - For the error message
38
+ * @returns {import("./scope.js").Evaluator}
39
+ */
40
+ function variableReader(depth, name) {
41
+ const check = (value) => {
42
+ if (value === undefined) {
43
+ throw new XPathError("XPDY0002", `No value is bound to $${name}`);
44
+ }
45
+ return value;
46
+ };
47
+ if (depth === 0) return (ctx) => check(ctx.env.value);
48
+ return (ctx) => {
49
+ let env = ctx.env;
50
+ for (let i = 0; i < depth; i++) env = env.next;
51
+ return check(env.value);
52
+ };
53
+ }
54
+
55
+ /** Compilers by node type. */
56
+ export const primaryCompilers = {
57
+ NumericLiteral(node) {
58
+ const result = Object.freeze([numericValue(node)]);
59
+ return () => result;
60
+ },
61
+
62
+ StringLiteral(node) {
63
+ const result = Object.freeze([new AtomicValue(types.string, node.value)]);
64
+ return () => result;
65
+ },
66
+
67
+ EmptySequence() {
68
+ return () => EMPTY;
69
+ },
70
+
71
+ ContextItemExpr() {
72
+ return (ctx) => [contextItem(ctx)];
73
+ },
74
+
75
+ VarRef(node, scope) {
76
+ const key = clark(namespaceOf(node.name, scope.sc, ""), node.name.local);
77
+ const depth = variableDepth(scope, key);
78
+ if (depth < 0) {
79
+ throw new XPathError(
80
+ "XPST0008",
81
+ `The variable $${node.name.local} is not declared`,
82
+ );
83
+ }
84
+ return variableReader(depth, node.name.local);
85
+ },
86
+
87
+ SequenceExpr(node, scope, compile) {
88
+ const parts = node.items.map((item) => compile(item, scope));
89
+ return (ctx) => {
90
+ const result = [];
91
+ for (const part of parts) {
92
+ for (const item of part(ctx)) result.push(item);
93
+ }
94
+ return result;
95
+ };
96
+ },
97
+ };
98
+
99
+ export { EMPTY };