@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,180 @@
1
+ /**
2
+ * Arithmetic on durations, dates and times (F&O 3.1 sections 8.2 and
3
+ * 10.8): duration ± duration, duration × / div number, duration div
4
+ * duration, date/time ± duration and date/time − date/time.
5
+ *
6
+ * @module @tradik/xslt3/xdm/temporalArithmetic
7
+ */
8
+
9
+ import { XPathError } from "../errors.js";
10
+ import { AtomicValue } from "./atomic.js";
11
+ import { daysInMonth } from "./calendar.js";
12
+ import { checkYear, componentsOf, DateTimeValue } from "./datetime.js";
13
+ import { fromLocalSeconds, localSeconds, timelineSeconds } from "./timeline.js";
14
+ import { Decimal } from "./decimal.js";
15
+ import { DurationValue } from "./duration.js";
16
+ import { numberToDecimal } from "./numeric.js";
17
+ import { derivesFrom, isNumericType, types } from "./types.js";
18
+
19
+ const HALF = Decimal.parse("0.5");
20
+ const ONE_DAY = Decimal.of(86400n);
21
+ /** Local seconds of midnight of the reference date of xs:time values. */
22
+ const REFERENCE_DAY = localSeconds(new DateTimeValue({}));
23
+
24
+ /**
25
+ * Operand category for the operator mapping.
26
+ * @param {AtomicValue} item
27
+ * @returns {string|null} "yearMonth", "dayTime", "dateTime", "date",
28
+ * "time", "numeric" or null
29
+ */
30
+ function category(item) {
31
+ if (derivesFrom(item.type, types.yearMonthDuration)) return "yearMonth";
32
+ if (derivesFrom(item.type, types.dayTimeDuration)) return "dayTime";
33
+ if (isNumericType(item.type)) return "numeric";
34
+ const primitive = item.type.primitive.localName;
35
+ return ["dateTime", "date", "time"].includes(primitive) ? primitive : null;
36
+ }
37
+
38
+ /**
39
+ * @param {"yearMonth"|"dayTime"} kind
40
+ * @param {Decimal} amount - Months (integral) or seconds
41
+ * @returns {AtomicValue}
42
+ */
43
+ function duration(kind, amount) {
44
+ return kind === "yearMonth"
45
+ ? new AtomicValue(
46
+ types.yearMonthDuration,
47
+ new DurationValue(amount.toNumber(), Decimal.ZERO),
48
+ )
49
+ : new AtomicValue(types.dayTimeDuration, new DurationValue(0, amount));
50
+ }
51
+
52
+ /** @param {AtomicValue} item @param {string} kind @returns {Decimal} */
53
+ const amountOf = (item, kind) =>
54
+ kind === "yearMonth"
55
+ ? Decimal.of(BigInt(item.value.months))
56
+ : item.value.seconds;
57
+
58
+ /**
59
+ * Numeric operand of duration × and div as an exact decimal.
60
+ * @param {AtomicValue} item
61
+ * @param {boolean} divide - Whether it is a divisor
62
+ * @returns {Decimal|null} null for an infinite divisor (result zero)
63
+ */
64
+ function factor(item, divide) {
65
+ const { value } = item;
66
+ if (typeof value === "bigint") return Decimal.of(value);
67
+ if (value instanceof Decimal) return value;
68
+ if (Number.isNaN(value)) {
69
+ throw new XPathError("FOCA0005", "NaN in duration arithmetic");
70
+ }
71
+ if (!Number.isFinite(value)) {
72
+ if (divide) return null;
73
+ throw new XPathError("FODT0002", "Duration overflow");
74
+ }
75
+ return numberToDecimal(value);
76
+ }
77
+
78
+ /** Months rounded half toward positive infinity (F&O 8.2.3). */
79
+ const roundMonths = (amount) => Decimal.of(amount.add(HALF).floor());
80
+
81
+ /** duration × number and duration div number. */
82
+ function scaleDuration(item, kind, op, number) {
83
+ const n = factor(number, op === "div");
84
+ if (n === null) return duration(kind, Decimal.ZERO);
85
+ if (op === "div" && n.sign() === 0) {
86
+ throw new XPathError("FODT0002", "Duration division by zero");
87
+ }
88
+ const amount =
89
+ op === "div" ? amountOf(item, kind).div(n) : amountOf(item, kind).mul(n);
90
+ return duration(kind, kind === "yearMonth" ? roundMonths(amount) : amount);
91
+ }
92
+
93
+ /**
94
+ * Adds a duration (signed) to a date, time or dateTime.
95
+ * @param {AtomicValue} item - xs:dateTime, xs:date or xs:time
96
+ * @param {string} kind - Its category
97
+ * @param {DurationValue} delta
98
+ * @returns {AtomicValue}
99
+ */
100
+ function addToDateTime(item, kind, delta) {
101
+ let value = item.value;
102
+ if (delta.months !== 0) {
103
+ const total = value.year * 12 + (value.month - 1) + delta.months;
104
+ const year = checkYear(Math.floor(total / 12));
105
+ const month = total - year * 12 + 1;
106
+ const day = Math.min(value.day, daysInMonth(year, month));
107
+ value = new DateTimeValue({ ...value, year, month, day });
108
+ }
109
+ let seconds = localSeconds(value).add(delta.seconds);
110
+ if (kind === "time") {
111
+ // wrap around midnight: seconds of the day modulo 86400
112
+ seconds = seconds.sub(REFERENCE_DAY).mod(ONE_DAY);
113
+ if (seconds.sign() < 0) seconds = seconds.add(ONE_DAY);
114
+ }
115
+ const result = fromLocalSeconds(seconds, value.timezone);
116
+ const fields = { timezone: value.timezone };
117
+ for (const name of componentsOf(kind)) fields[name] = result[name];
118
+ return new AtomicValue(types[kind], new DateTimeValue(fields));
119
+ }
120
+
121
+ /**
122
+ * Applies a date/time or duration operator (appendix B.2 mapping).
123
+ * @param {AtomicValue} a - Already prepared (no xs:untypedAtomic)
124
+ * @param {string} op
125
+ * @param {AtomicValue} b
126
+ * @param {{implicitTimezone?: number}} options
127
+ * @returns {AtomicValue}
128
+ * @throws {XPathError} XPTY0004 when no operator matches the types
129
+ */
130
+ export function temporalArithmetic(a, op, b, options) {
131
+ const ka = category(a);
132
+ const kb = category(b);
133
+ const durations = ["yearMonth", "dayTime"];
134
+ const times = ["dateTime", "date", "time"];
135
+ const sameDuration = ka === kb && durations.includes(ka);
136
+ if (sameDuration && (op === "+" || op === "-")) {
137
+ const delta = op === "+" ? amountOf(b, kb) : amountOf(b, kb).neg();
138
+ return duration(ka, amountOf(a, ka).add(delta));
139
+ }
140
+ if (sameDuration && op === "div") {
141
+ if (amountOf(b, kb).sign() === 0) {
142
+ throw new XPathError("FOAR0001", "Duration division by zero");
143
+ }
144
+ return new AtomicValue(types.decimal, amountOf(a, ka).div(amountOf(b, kb)));
145
+ }
146
+ if (
147
+ durations.includes(ka) &&
148
+ kb === "numeric" &&
149
+ (op === "*" || op === "div")
150
+ ) {
151
+ return scaleDuration(a, ka, op, b);
152
+ }
153
+ if (ka === "numeric" && durations.includes(kb) && op === "*") {
154
+ return scaleDuration(b, kb, op, a);
155
+ }
156
+ const timeOk = (t, d) =>
157
+ times.includes(t) &&
158
+ durations.includes(d) &&
159
+ !(t === "time" && d === "yearMonth");
160
+ if (timeOk(ka, kb) && (op === "+" || op === "-")) {
161
+ const { months, seconds } = b.value;
162
+ const delta =
163
+ op === "+" ? b.value : new DurationValue(-months, seconds.neg());
164
+ return addToDateTime(a, ka, delta);
165
+ }
166
+ if (timeOk(kb, ka) && op === "+") return addToDateTime(b, kb, a.value);
167
+ if (ka === kb && times.includes(ka) && op === "-") {
168
+ const timezone = options.implicitTimezone ?? 0;
169
+ return duration(
170
+ "dayTime",
171
+ timelineSeconds(a.value, timezone).sub(
172
+ timelineSeconds(b.value, timezone),
173
+ ),
174
+ );
175
+ }
176
+ throw new XPathError(
177
+ "XPTY0004",
178
+ `${op} is not defined for ${a.type.prefixedName} and ${b.type.prefixedName}`,
179
+ );
180
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Positions of date/time values on the time line, in seconds, for
3
+ * comparison and arithmetic (F&O 3.1 sections 10.4 and 10.8).
4
+ *
5
+ * @module @tradik/xslt3/xdm/timeline
6
+ */
7
+
8
+ import { civilFromDays, daysFromCivil } from "./calendar.js";
9
+ import { checkYear, DateTimeValue } from "./datetime.js";
10
+ import { Decimal } from "./decimal.js";
11
+
12
+ /**
13
+ * Seconds of the local value (ignoring the timezone) since
14
+ * 1970-01-01T00:00:00, with the reference date 1972-01-01 for absent date
15
+ * components (F&O 3.1 section 10.4 uses 1972 as reference year; the
16
+ * reference month and day do not change comparisons within one type).
17
+ * @param {DateTimeValue} value
18
+ * @returns {Decimal}
19
+ */
20
+ export function localSeconds(value) {
21
+ const days = daysFromCivil(
22
+ value.year ?? 1972,
23
+ value.month ?? 1,
24
+ value.day ?? 1,
25
+ );
26
+ const whole =
27
+ BigInt(days) * 86400n +
28
+ BigInt((value.hour ?? 0) * 3600 + (value.minute ?? 0) * 60);
29
+ return Decimal.of(whole).add(value.second ?? Decimal.ZERO);
30
+ }
31
+
32
+ /**
33
+ * Position on the time line in seconds (UTC), using the implicit timezone
34
+ * when the value has none.
35
+ * @param {DateTimeValue} value
36
+ * @param {number} implicitTimezone - Minutes
37
+ * @returns {Decimal}
38
+ */
39
+ export function timelineSeconds(value, implicitTimezone) {
40
+ const offset = value.timezone ?? implicitTimezone;
41
+ return localSeconds(value).sub(Decimal.of(BigInt(offset * 60)));
42
+ }
43
+
44
+ /**
45
+ * Date and time components of a local-seconds value (inverse of
46
+ * {@link localSeconds} for full date-times).
47
+ * @param {Decimal} seconds
48
+ * @param {number|null} timezone - Kept as the timezone of the result
49
+ * @returns {DateTimeValue}
50
+ * @throws {XPathError} FODT0001 when the year is out of range
51
+ */
52
+ export function fromLocalSeconds(seconds, timezone) {
53
+ const wholeSeconds = seconds.floor();
54
+ const days = wholeSeconds / 86400n - (wholeSeconds % 86400n < 0n ? 1n : 0n);
55
+ const inDay = Number(wholeSeconds - days * 86400n);
56
+ const [year, month, day] = civilFromDays(Number(days));
57
+ return new DateTimeValue({
58
+ year: checkYear(year),
59
+ month,
60
+ day,
61
+ hour: Math.floor(inDay / 3600),
62
+ minute: Math.floor(inDay / 60) % 60,
63
+ second: Decimal.of(BigInt(inDay % 60)).add(
64
+ seconds.sub(Decimal.of(wholeSeconds)),
65
+ ),
66
+ timezone,
67
+ });
68
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Registry of the built-in atomic types of XDM 3.1 for a basic
3
+ * (non-schema-aware) processor, with the XSD 1.1 derivation hierarchy.
4
+ *
5
+ * A type descriptor has:
6
+ * - `name`: expanded QName in Clark notation, `{http://www.w3.org/2001/XMLSchema}integer`
7
+ * - `localName`, `prefixedName` ("xs:integer")
8
+ * - `base`: the descriptor it is derived from (null for xs:anyAtomicType and xs:error)
9
+ * - `primitive`: the XSD primitive ancestor-or-self (xs:integer → xs:decimal);
10
+ * xs:untypedAtomic is its own primitive
11
+ * - `castPrimitive`: the "primitive type" of F&O 3.1 section 19, which also
12
+ * counts xs:integer, xs:yearMonthDuration and xs:dayTimeDuration
13
+ * - `whiteSpace`: "preserve", "replace" or "collapse"
14
+ * - `abstract`: true for types with no direct instances
15
+ * - `facets`: `{ minInclusive?, maxInclusive? }` (BigInt) for integer types,
16
+ * `{ pattern?: RegExp }` for string types, `{ timezoneRequired? }`
17
+ *
18
+ * @module @tradik/xslt3/xdm/types
19
+ */
20
+
21
+ import { XPathError } from "../errors.js";
22
+
23
+ /** The XML Schema namespace URI. */
24
+ export const XS_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
25
+
26
+ const nameStart =
27
+ "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF" +
28
+ "\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF" +
29
+ "\\uFDF0-\\uFFFD\\u{10000}-\\u{EFFFF}";
30
+ const nameChar = `${nameStart}\\-.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040`;
31
+
32
+ /** Patterns of the XML name productions (XML 1.0 fifth edition). */
33
+ /* eslint-disable no-misleading-character-class -- NameChar includes the
34
+ combining marks U+0300 to U+036F on their own, as the XML grammar says */
35
+ export const namePatterns = Object.freeze({
36
+ ncName: new RegExp(`^[${nameStart}][${nameChar}]*$`, "u"),
37
+ name: new RegExp(`^[:${nameStart}][:${nameChar}]*$`, "u"),
38
+ nmToken: new RegExp(`^[:${nameChar}]+$`, "u"),
39
+ });
40
+ /* eslint-enable no-misleading-character-class */
41
+
42
+ /** @type {Map<string, object>} descriptors by local name */
43
+ const byLocalName = new Map();
44
+
45
+ /**
46
+ * Registers a built-in type.
47
+ * @param {string} localName
48
+ * @param {string|null} baseName
49
+ * @param {object} [options] - whiteSpace, abstract, facets, castPrimitive
50
+ */
51
+ function define(localName, baseName, options = {}) {
52
+ const base = baseName === null ? null : byLocalName.get(baseName);
53
+ const type = {
54
+ name: `{${XS_NAMESPACE}}${localName}`,
55
+ localName,
56
+ prefixedName: `xs:${localName}`,
57
+ base,
58
+ whiteSpace: options.whiteSpace ?? base?.whiteSpace ?? "collapse",
59
+ abstract: options.abstract ?? false,
60
+ facets: Object.freeze({ ...options.facets }),
61
+ };
62
+ const primitiveRoot = base === null || base.localName === "anyAtomicType";
63
+ type.primitive = primitiveRoot ? type : base.primitive;
64
+ type.castPrimitive =
65
+ options.castPrimitive || primitiveRoot ? type : base.castPrimitive;
66
+ byLocalName.set(localName, Object.freeze(type));
67
+ }
68
+
69
+ const range = (min, max) => ({
70
+ facets: { minInclusive: min ?? undefined, maxInclusive: max ?? undefined },
71
+ });
72
+
73
+ define("anyAtomicType", null, { abstract: true });
74
+ define("error", null);
75
+ define("untypedAtomic", "anyAtomicType", { whiteSpace: "preserve" });
76
+ define("string", "anyAtomicType", { whiteSpace: "preserve" });
77
+ define("normalizedString", "string", { whiteSpace: "replace" });
78
+ define("token", "normalizedString", { whiteSpace: "collapse" });
79
+ define("language", "token", {
80
+ facets: { pattern: /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/ },
81
+ });
82
+ define("NMTOKEN", "token", { facets: { pattern: namePatterns.nmToken } });
83
+ define("Name", "token", { facets: { pattern: namePatterns.name } });
84
+ define("NCName", "Name", { facets: { pattern: namePatterns.ncName } });
85
+ for (const name of ["ID", "IDREF", "ENTITY"]) define(name, "NCName");
86
+ define("boolean", "anyAtomicType");
87
+ define("decimal", "anyAtomicType");
88
+ define("integer", "decimal", { castPrimitive: true });
89
+ define("nonPositiveInteger", "integer", range(null, 0n));
90
+ define("negativeInteger", "nonPositiveInteger", range(null, -1n));
91
+ define("long", "integer", range(-(2n ** 63n), 2n ** 63n - 1n));
92
+ define("int", "long", range(-(2n ** 31n), 2n ** 31n - 1n));
93
+ define("short", "int", range(-32768n, 32767n));
94
+ define("byte", "short", range(-128n, 127n));
95
+ define("nonNegativeInteger", "integer", range(0n, null));
96
+ define("unsignedLong", "nonNegativeInteger", range(0n, 2n ** 64n - 1n));
97
+ define("unsignedInt", "unsignedLong", range(0n, 2n ** 32n - 1n));
98
+ define("unsignedShort", "unsignedInt", range(0n, 65535n));
99
+ define("unsignedByte", "unsignedShort", range(0n, 255n));
100
+ define("positiveInteger", "nonNegativeInteger", range(1n, null));
101
+ define("float", "anyAtomicType");
102
+ define("double", "anyAtomicType");
103
+ define("duration", "anyAtomicType");
104
+ define("yearMonthDuration", "duration", { castPrimitive: true });
105
+ define("dayTimeDuration", "duration", { castPrimitive: true });
106
+ define("dateTime", "anyAtomicType");
107
+ define("dateTimeStamp", "dateTime", { facets: { timezoneRequired: true } });
108
+ for (const name of [
109
+ "date",
110
+ "time",
111
+ "gYearMonth",
112
+ "gYear",
113
+ "gMonthDay",
114
+ "gDay",
115
+ "gMonth",
116
+ ]) {
117
+ define(name, "anyAtomicType");
118
+ }
119
+ define("hexBinary", "anyAtomicType");
120
+ define("base64Binary", "anyAtomicType");
121
+ define("anyURI", "anyAtomicType");
122
+ define("QName", "anyAtomicType");
123
+ define("NOTATION", "anyAtomicType", { abstract: true });
124
+
125
+ const clarkPattern = new RegExp(
126
+ `^\\{${XS_NAMESPACE.replaceAll(".", "\\.")}\\}(.+)$`,
127
+ );
128
+
129
+ /**
130
+ * Looks up a built-in atomic type.
131
+ * @param {string|object} name - A descriptor, "xs:integer", "integer" or
132
+ * "{http://www.w3.org/2001/XMLSchema}integer"
133
+ * @returns {object} the type descriptor
134
+ * @throws {XPathError} XPST0051 for an unknown type name
135
+ */
136
+ export function getType(name) {
137
+ if (typeof name === "object" && name !== null) return name;
138
+ const localName = String(name)
139
+ .replace(/^xs:/, "")
140
+ .replace(clarkPattern, "$1");
141
+ const type = byLocalName.get(localName);
142
+ if (!type) throw new XPathError("XPST0051", `Unknown atomic type ${name}`);
143
+ return type;
144
+ }
145
+
146
+ /**
147
+ * Whether a type is the same as or derived (by restriction) from another.
148
+ * Every atomic type derives from xs:anyAtomicType; xs:error from nothing.
149
+ * @param {object|string} type
150
+ * @param {object|string} ancestor
151
+ * @returns {boolean}
152
+ */
153
+ export function derivesFrom(type, ancestor) {
154
+ const target = getType(ancestor);
155
+ for (let t = getType(type); t !== null; t = t.base) {
156
+ if (t === target) return true;
157
+ }
158
+ return false;
159
+ }
160
+
161
+ /** @returns {object[]} all registered type descriptors */
162
+ export function allTypes() {
163
+ return [...byLocalName.values()];
164
+ }
165
+
166
+ /** @type {Readonly<Record<string, object>>} descriptors by local name */
167
+ export const types = Object.freeze(Object.fromEntries(byLocalName));
168
+
169
+ const numericNames = new Set(["decimal", "float", "double"]);
170
+
171
+ /**
172
+ * Whether a type is numeric (xs:decimal, xs:float, xs:double or derived).
173
+ * @param {object} type
174
+ * @returns {boolean}
175
+ */
176
+ export function isNumericType(type) {
177
+ return numericNames.has(type.primitive.localName);
178
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Shorthands for building atomic values of the common types and for
3
+ * reading single values out of sequences.
4
+ *
5
+ * @module @tradik/xslt3/xpath/eval/atomics
6
+ */
7
+
8
+ import { XPathError } from "../../errors.js";
9
+ import { AtomicValue } from "../../xdm/atomic.js";
10
+ import { atomize } from "../../xdm/nodes.js";
11
+ import { types } from "../../xdm/types.js";
12
+
13
+ const TRUE = new AtomicValue(types.boolean, true);
14
+ const FALSE = new AtomicValue(types.boolean, false);
15
+
16
+ /** @param {boolean} value @returns {AtomicValue} an xs:boolean */
17
+ export const booleanItem = (value) => (value ? TRUE : FALSE);
18
+
19
+ /** @param {bigint|number} value @returns {AtomicValue} an xs:integer */
20
+ export const integerItem = (value) =>
21
+ new AtomicValue(types.integer, BigInt(value));
22
+
23
+ /** @param {string} value @returns {AtomicValue} an xs:string */
24
+ export const stringItem = (value) => new AtomicValue(types.string, value);
25
+
26
+ /** @param {number} value @returns {AtomicValue} an xs:double */
27
+ export const doubleItem = (value) => new AtomicValue(types.double, value);
28
+
29
+ /**
30
+ * Atomizes a sequence that must hold at most one item.
31
+ * @param {Array} sequence
32
+ * @param {string} what - Operand description for the error message
33
+ * @returns {AtomicValue|undefined} the atomic value, undefined when empty
34
+ * @throws {XPathError} XPTY0004 for more than one item
35
+ */
36
+ export function atomizeOptional(sequence, what) {
37
+ const values = atomize(sequence);
38
+ if (values.length > 1) {
39
+ throw new XPathError(
40
+ "XPTY0004",
41
+ `The ${what} must not be a sequence of more than one item`,
42
+ );
43
+ }
44
+ return values[0];
45
+ }
@@ -0,0 +1,212 @@
1
+ /**
2
+ * The thirteen axes of XPath 3.1 (section 3.3.2.1) over DOM nodes. An axis
3
+ * function collects the nodes that pass a node test, in axis order:
4
+ * document order for the forward axes, reverse document order for the
5
+ * reverse axes (parent, ancestor, ancestor-or-self, preceding,
6
+ * preceding-sibling). It stops after `limit` nodes, so that `[1]` or `[2]`
7
+ * after a step does not walk the whole axis.
8
+ *
9
+ * Siblings are reached through nextSibling/previousSibling, so walking an
10
+ * axis from each of n siblings stays linear when it stops early.
11
+ *
12
+ * @module @tradik/xslt3/xpath/eval/axes
13
+ */
14
+
15
+ import {
16
+ attributesOf,
17
+ isContainer,
18
+ isXdmNode,
19
+ namedAttributeOf,
20
+ parentOf,
21
+ } from "./domNodes.js";
22
+ import { namespaceNodesOf } from "./namespaceNodes.js";
23
+
24
+ /** Axes whose order is reverse document order. */
25
+ export const REVERSE_AXES = new Set([
26
+ "parent",
27
+ "ancestor",
28
+ "ancestor-or-self",
29
+ "preceding",
30
+ "preceding-sibling",
31
+ ]);
32
+
33
+ /**
34
+ * @callback Axis
35
+ * @param {Node} node - Context node
36
+ * @param {(node: Node) => boolean} test - Node test, false for the nodes
37
+ * that are not XDM nodes (document types; see nodeTests.js)
38
+ * @param {number} limit - Most nodes to collect
39
+ * @returns {Node[]} the nodes in axis order
40
+ */
41
+
42
+ /** @param {Node} node @returns {boolean} attribute or namespace node */
43
+ const isAttached = (node) => node.nodeType === 2 || node.nodeType === 13;
44
+
45
+ /**
46
+ * Collects the descendants of a node in document order, walking
47
+ * firstChild/nextSibling/parentNode: no child arrays, and each DOM property
48
+ * read once per node (in jsdom every read goes through a wrapper, and
49
+ * childNodes through a Proxy).
50
+ * @param {Node} node
51
+ * @param {Function} test - Rejects the nodes that are not XDM nodes
52
+ * @param {number} limit
53
+ * @param {Node[]} result - Appended to
54
+ * @returns {boolean} true when the limit is reached
55
+ */
56
+ function descendants(node, test, limit, result) {
57
+ if (!isContainer(node)) return false;
58
+ let current = node.firstChild;
59
+ while (current) {
60
+ if (test(current) && result.push(current) >= limit) return true;
61
+ if (current.nodeType === 1) {
62
+ const first = current.firstChild;
63
+ if (first) {
64
+ current = first;
65
+ continue;
66
+ }
67
+ }
68
+ let next = current.nextSibling;
69
+ while (!next) {
70
+ current = current.parentNode;
71
+ if (current === node || !current) return false;
72
+ next = current.nextSibling;
73
+ }
74
+ current = next;
75
+ }
76
+ return false;
77
+ }
78
+
79
+ /**
80
+ * The next or previous XDM sibling of a node.
81
+ * @param {Node} node
82
+ * @param {"nextSibling"|"previousSibling"} direction
83
+ * @returns {Node|null}
84
+ */
85
+ function sibling(node, direction) {
86
+ let current = node[direction];
87
+ while (current && !isXdmNode(current)) {
88
+ current = current[direction];
89
+ }
90
+ return current ?? null;
91
+ }
92
+
93
+ /**
94
+ * Collects the siblings of a node in one direction.
95
+ * @returns {Node[]}
96
+ */
97
+ function siblings(node, test, limit, direction) {
98
+ const result = [];
99
+ if (isAttached(node)) return result;
100
+ for (let s = sibling(node, direction); s; s = sibling(s, direction)) {
101
+ if (test(s) && result.push(s) >= limit) break;
102
+ }
103
+ return result;
104
+ }
105
+
106
+ /**
107
+ * Collects a list of candidate nodes.
108
+ * @param {Iterable<Node>} nodes
109
+ * @returns {Node[]}
110
+ */
111
+ function collect(nodes, test, limit) {
112
+ const result = [];
113
+ for (const node of nodes) {
114
+ if (test(node) && result.push(node) >= limit) break;
115
+ }
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ * The ancestors of a node, nearest first, optionally with the node.
121
+ * @param {Node} node
122
+ * @param {boolean} self
123
+ * @returns {Node[]}
124
+ */
125
+ function ancestry(node, self) {
126
+ const chain = self ? [node] : [];
127
+ for (let p = parentOf(node); p; p = parentOf(p)) chain.push(p);
128
+ return chain;
129
+ }
130
+
131
+ /** @type {Record<string, Axis>} Axis functions by axis name. */
132
+ export const axes = {
133
+ self: (node, test) => (test(node) ? [node] : []),
134
+ child(node, test, limit) {
135
+ const result = [];
136
+ if (!isContainer(node)) return result;
137
+ for (let c = node.firstChild; c; c = c.nextSibling) {
138
+ if (test(c) && result.push(c) >= limit) break;
139
+ }
140
+ return result;
141
+ },
142
+ attribute(node, test, limit) {
143
+ if (test.qname) {
144
+ const found = namedAttributeOf(node, test.qname.uri, test.qname.local);
145
+ if (found !== undefined) {
146
+ return found && limit > 0 && test(found) ? [found] : [];
147
+ }
148
+ }
149
+ return collect(attributesOf(node), test, limit);
150
+ },
151
+ namespace: (node, test, limit) =>
152
+ node.nodeType === 1 ? collect(namespaceNodesOf(node), test, limit) : [],
153
+ parent(node, test) {
154
+ const parent = parentOf(node);
155
+ return parent && test(parent) ? [parent] : [];
156
+ },
157
+ descendant(node, test, limit) {
158
+ const result = [];
159
+ descendants(node, test, limit, result);
160
+ return result;
161
+ },
162
+ "descendant-or-self"(node, test, limit) {
163
+ const result = test(node) ? [node] : [];
164
+ if (result.length < limit) descendants(node, test, limit, result);
165
+ return result;
166
+ },
167
+ ancestor: (node, test, limit) => collect(ancestry(node, false), test, limit),
168
+ "ancestor-or-self": (node, test, limit) =>
169
+ collect(ancestry(node, true), test, limit),
170
+ "following-sibling": (node, test, limit) =>
171
+ siblings(node, test, limit, "nextSibling"),
172
+ "preceding-sibling": (node, test, limit) =>
173
+ siblings(node, test, limit, "previousSibling"),
174
+ following(node, test, limit) {
175
+ const result = [];
176
+ let start = node;
177
+ if (isAttached(node)) {
178
+ start = parentOf(node);
179
+ if (!start || descendants(start, test, limit, result)) return result;
180
+ }
181
+ for (let n = start; n; n = parentOf(n)) {
182
+ for (
183
+ let s = sibling(n, "nextSibling");
184
+ s;
185
+ s = sibling(s, "nextSibling")
186
+ ) {
187
+ if (test(s) && result.push(s) >= limit) return result;
188
+ if (descendants(s, test, limit, result)) return result;
189
+ }
190
+ }
191
+ return result;
192
+ },
193
+ preceding(node, test, limit) {
194
+ const result = [];
195
+ const start = isAttached(node) ? parentOf(node) : node;
196
+ for (let n = start; n; n = parentOf(n)) {
197
+ for (
198
+ let s = sibling(n, "previousSibling");
199
+ s;
200
+ s = sibling(s, "previousSibling")
201
+ ) {
202
+ const subtree = [];
203
+ descendants(s, test, Infinity, subtree);
204
+ for (let i = subtree.length - 1; i >= 0; i--) {
205
+ if (result.push(subtree[i]) >= limit) return result;
206
+ }
207
+ if (test(s) && result.push(s) >= limit) return result;
208
+ }
209
+ }
210
+ return result;
211
+ },
212
+ };