@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,47 @@
1
+ /**
2
+ * The XPath and XQuery Data Model 3.1 for a basic (non-schema-aware)
3
+ * processor: atomic types and values, casting, comparisons, arithmetic,
4
+ * atomization and effective boolean value.
5
+ *
6
+ * @module @tradik/xslt3/xdm
7
+ */
8
+
9
+ export { XPathError } from "../errors.js";
10
+ export {
11
+ allTypes,
12
+ derivesFrom,
13
+ getType,
14
+ isNumericType,
15
+ namePatterns,
16
+ types,
17
+ XS_NAMESPACE,
18
+ } from "./types.js";
19
+ export {
20
+ atomic,
21
+ AtomicValue,
22
+ checkFacets,
23
+ isArray,
24
+ isAtomic,
25
+ isFunctionItem,
26
+ isMap,
27
+ isNode,
28
+ ITEM_KIND,
29
+ itemKind,
30
+ } from "./atomic.js";
31
+ export { Decimal, DIVISION_SCALE } from "./decimal.js";
32
+ export { DateTimeValue, MAX_YEAR } from "./datetime.js";
33
+ export { DurationValue } from "./duration.js";
34
+ export { QNameValue } from "./qname.js";
35
+ export { canonicalString, fromLexical } from "./lexical.js";
36
+ export { cast, castable } from "./cast.js";
37
+ export { compareAtomic, deepEqualAtomic, valueCompare } from "./compare.js";
38
+ export { generalCompare, toNumber } from "./generalCompare.js";
39
+ export { arithmetic, unaryArithmetic } from "./arithmetic.js";
40
+ export {
41
+ atomize,
42
+ effectiveBooleanValue,
43
+ nodeStringValue,
44
+ stringValue,
45
+ typedValue,
46
+ } from "./nodes.js";
47
+ export { compareCodepoints, normalizeWhitespace } from "./strings.js";
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Lexical spaces of all built-in atomic types: parsing a string into a
3
+ * typed value (F&O 3.1 19.2, casting from xs:string) and the canonical
4
+ * string of a value (F&O 3.1 19.1.1, casting to xs:string).
5
+ *
6
+ * @module @tradik/xslt3/xdm/lexical
7
+ */
8
+
9
+ import { XPathError } from "../errors.js";
10
+ import { AtomicValue, checkFacets } from "./atomic.js";
11
+ import {
12
+ formatBase64Binary,
13
+ formatHexBinary,
14
+ parseBase64Binary,
15
+ parseHexBinary,
16
+ } from "./binary.js";
17
+ import { formatDateTime, parseDateTime } from "./datetime.js";
18
+ import { formatDuration, parseDuration } from "./duration.js";
19
+ import {
20
+ formatDouble,
21
+ parseDecimal,
22
+ parseDouble,
23
+ parseFloat32,
24
+ parseInteger,
25
+ } from "./numeric.js";
26
+ import { parseQName } from "./qname.js";
27
+ import { normalizeWhitespace, parseBoolean } from "./strings.js";
28
+ import { getType } from "./types.js";
29
+
30
+ const identity = (text) => text;
31
+ const durationKinds = ["duration", "yearMonthDuration", "dayTimeDuration"];
32
+ const dateTimeKinds = [
33
+ "dateTime",
34
+ "date",
35
+ "time",
36
+ "gYearMonth",
37
+ "gYear",
38
+ "gMonthDay",
39
+ "gDay",
40
+ "gMonth",
41
+ ];
42
+
43
+ /** Parsers by cast-primitive local name: (text, kind, options) → value | null. */
44
+ const parsers = {
45
+ untypedAtomic: identity,
46
+ string: identity,
47
+ anyURI: identity,
48
+ boolean: parseBoolean,
49
+ decimal: parseDecimal,
50
+ integer: parseInteger,
51
+ double: parseDouble,
52
+ float: parseFloat32,
53
+ hexBinary: parseHexBinary,
54
+ base64Binary: parseBase64Binary,
55
+ QName: (text, _kind, options) => parseQName(text, options.resolveNamespace),
56
+ };
57
+ for (const kind of durationKinds) {
58
+ parsers[kind] = (text) => parseDuration(kind, text);
59
+ }
60
+ for (const kind of dateTimeKinds) {
61
+ parsers[kind] = (text) => parseDateTime(kind, text);
62
+ }
63
+
64
+ /** Formatters by cast-primitive local name: value → canonical string. */
65
+ const formatters = {
66
+ untypedAtomic: identity,
67
+ string: identity,
68
+ anyURI: identity,
69
+ boolean: String,
70
+ decimal: String,
71
+ integer: String,
72
+ double: (value) => formatDouble(value),
73
+ float: (value) => formatDouble(value, true),
74
+ hexBinary: formatHexBinary,
75
+ base64Binary: formatBase64Binary,
76
+ QName: String,
77
+ NOTATION: String,
78
+ };
79
+ for (const kind of durationKinds) {
80
+ formatters[kind] = (value) => formatDuration(kind, value);
81
+ }
82
+ for (const kind of dateTimeKinds) {
83
+ formatters[kind] = (value) => formatDateTime(kind, value);
84
+ }
85
+
86
+ /**
87
+ * Converts a string to a value of an atomic type by the rules of XSD
88
+ * validation: whiteSpace facet, lexical space, then the other facets.
89
+ * @param {string|object} typeName - Target type
90
+ * @param {string} text
91
+ * @param {object} [options]
92
+ * @param {import("./qname.js").NamespaceResolver} [options.resolveNamespace]
93
+ * - Prefix resolver for xs:QName
94
+ * @returns {AtomicValue}
95
+ * @throws {XPathError} XPST0080 for abstract types, FORG0001 for invalid
96
+ * text, FODT0001/FODT0002 for out-of-range dates/durations, FONS0004 for
97
+ * unbound QName prefixes
98
+ */
99
+ export function fromLexical(typeName, text, options = {}) {
100
+ const type = getType(typeName);
101
+ if (type.abstract) {
102
+ throw new XPathError("XPST0080", `Cannot cast to ${type.prefixedName}`);
103
+ }
104
+ const parse = parsers[type.castPrimitive.localName];
105
+ const normalized = normalizeWhitespace(text, type.whiteSpace);
106
+ const value = parse
107
+ ? parse(normalized, type.castPrimitive.localName, options)
108
+ : null;
109
+ if (value === null) {
110
+ throw new XPathError(
111
+ "FORG0001",
112
+ `"${text}" is not a valid ${type.prefixedName}`,
113
+ );
114
+ }
115
+ return new AtomicValue(type, checkFacets(type, value));
116
+ }
117
+
118
+ /**
119
+ * Canonical string of an atomic value (the result of casting it to
120
+ * xs:string).
121
+ * @param {AtomicValue} item
122
+ * @returns {string}
123
+ */
124
+ export function canonicalString(item) {
125
+ return formatters[item.type.castPrimitive.localName](item.value);
126
+ }
@@ -0,0 +1,170 @@
1
+ /**
2
+ * Accessors of the data model for any item: string value, typed value
3
+ * (atomization) and effective boolean value.
4
+ *
5
+ * Nodes are DOM nodes of any implementation; only `nodeType`, `nodeValue`
6
+ * and `firstChild`/`nextSibling` (else `childNodes`) are read. Without a schema, elements, attributes, text
7
+ * and document nodes have an xs:untypedAtomic typed value; comments,
8
+ * processing instructions and namespace nodes an xs:string one.
9
+ *
10
+ * @module @tradik/xslt3/xdm/nodes
11
+ */
12
+
13
+ import { XPathError } from "../errors.js";
14
+ import { AtomicValue, itemKind } from "./atomic.js";
15
+ import { canonicalString } from "./lexical.js";
16
+ import { types } from "./types.js";
17
+
18
+ const TEXT_NODE = 3;
19
+ const CDATA_SECTION_NODE = 4;
20
+ const PROCESSING_INSTRUCTION_NODE = 7;
21
+ const COMMENT_NODE = 8;
22
+ /** nodeType used for XPath namespace nodes by DOM-based XPath engines. */
23
+ const NAMESPACE_NODE = 13;
24
+ const ELEMENT_NODE = 1;
25
+ const DOCUMENT_NODE = 9;
26
+ const DOCUMENT_FRAGMENT_NODE = 11;
27
+ const containers = new Set([
28
+ ELEMENT_NODE,
29
+ DOCUMENT_NODE,
30
+ DOCUMENT_FRAGMENT_NODE,
31
+ ]);
32
+
33
+ /**
34
+ * Appends the text of the descendant text nodes of a node.
35
+ * @param {Node} node
36
+ * @param {string[]} parts
37
+ */
38
+ function collectText(node, parts) {
39
+ // firstChild/nextSibling: childNodes is a live list behind a Proxy in
40
+ // jsdom, several times slower to iterate
41
+ if (node.firstChild !== undefined) {
42
+ for (let child = node.firstChild; child; child = child.nextSibling) {
43
+ const type = child.nodeType;
44
+ if (type === TEXT_NODE || type === CDATA_SECTION_NODE) {
45
+ parts.push(child.nodeValue);
46
+ } else if (type === ELEMENT_NODE) {
47
+ collectText(child, parts);
48
+ }
49
+ }
50
+ return;
51
+ }
52
+ for (const child of node.childNodes) {
53
+ if (child.nodeType === TEXT_NODE || child.nodeType === CDATA_SECTION_NODE) {
54
+ parts.push(child.nodeValue);
55
+ } else if (child.nodeType === ELEMENT_NODE) {
56
+ collectText(child, parts);
57
+ }
58
+ }
59
+ }
60
+
61
+ /**
62
+ * String value of a node (dm:string-value): the concatenated descendant
63
+ * text of elements and documents, the value of other nodes.
64
+ * @param {Node} node
65
+ * @returns {string}
66
+ */
67
+ export function nodeStringValue(node) {
68
+ return stringValueOf(node, node.nodeType);
69
+ }
70
+
71
+ /**
72
+ * nodeStringValue() of a node whose nodeType was read already.
73
+ * @param {Node} node
74
+ * @param {number} type - Its nodeType
75
+ * @returns {string}
76
+ */
77
+ function stringValueOf(node, type) {
78
+ if (!containers.has(type)) return node.nodeValue ?? "";
79
+ const first = node.firstChild;
80
+ if (first && first.nextSibling === null) {
81
+ // A single child, typically the text of a leaf element
82
+ const type = first.nodeType;
83
+ if (type === TEXT_NODE || type === CDATA_SECTION_NODE) {
84
+ return first.nodeValue;
85
+ }
86
+ }
87
+ const parts = [];
88
+ collectText(node, parts);
89
+ return parts.join("");
90
+ }
91
+
92
+ /** Node types whose typed value is an xs:string. */
93
+ const stringTyped = new Set([
94
+ PROCESSING_INSTRUCTION_NODE,
95
+ COMMENT_NODE,
96
+ NAMESPACE_NODE,
97
+ ]);
98
+
99
+ /**
100
+ * Typed value of a node (dm:typed-value) in a non-schema-aware processor.
101
+ * @param {Node} node
102
+ * @returns {AtomicValue}
103
+ */
104
+ export function typedValue(node) {
105
+ const nodeType = node.nodeType;
106
+ const type = stringTyped.has(nodeType) ? types.string : types.untypedAtomic;
107
+ return new AtomicValue(type, stringValueOf(node, nodeType));
108
+ }
109
+
110
+ /**
111
+ * Atomizes a sequence (fn:data): atomic values are kept, nodes give their
112
+ * typed value, arrays are flattened recursively.
113
+ * @param {Array} sequence
114
+ * @returns {AtomicValue[]}
115
+ * @throws {XPathError} FOTY0013 for maps and function items
116
+ */
117
+ export function atomize(sequence) {
118
+ const result = [];
119
+ for (const item of sequence) {
120
+ const kind = itemKind(item);
121
+ if (kind === "atomic") result.push(item);
122
+ else if (kind === "node") result.push(typedValue(item));
123
+ else if (kind === "array") result.push(...atomize(item.members.flat()));
124
+ else throw new XPathError("FOTY0013", `A ${kind} cannot be atomized`);
125
+ }
126
+ return result;
127
+ }
128
+
129
+ /**
130
+ * String value of an item (fn:string).
131
+ * @param {*} item - Atomic value or node
132
+ * @returns {string}
133
+ * @throws {XPathError} FOTY0014 for maps, arrays and function items
134
+ */
135
+ export function stringValue(item) {
136
+ const kind = itemKind(item);
137
+ if (kind === "atomic") return canonicalString(item);
138
+ if (kind === "node") return nodeStringValue(item);
139
+ throw new XPathError("FOTY0014", `A ${kind} has no string value`);
140
+ }
141
+
142
+ const stringPrimitives = new Set(["string", "anyURI", "untypedAtomic"]);
143
+ const numericPrimitives = new Set(["decimal", "float", "double"]);
144
+
145
+ /**
146
+ * Effective boolean value of a sequence (fn:boolean, XPath 3.1 2.4.3).
147
+ * @param {Array} sequence
148
+ * @returns {boolean}
149
+ * @throws {XPathError} FORG0006 when the sequence has no EBV
150
+ */
151
+ export function effectiveBooleanValue(sequence) {
152
+ if (sequence.length === 0) return false;
153
+ const first = sequence[0];
154
+ const kind = itemKind(first);
155
+ if (kind === "node") return true;
156
+ if (kind === "atomic" && sequence.length === 1) {
157
+ const { type, value } = first;
158
+ const primitive = type.primitive.localName;
159
+ if (primitive === "boolean") return value;
160
+ if (stringPrimitives.has(primitive)) return value.length > 0;
161
+ if (numericPrimitives.has(primitive)) {
162
+ if (typeof value === "object") return value.sign() !== 0;
163
+ return !(Number.isNaN(value) || value === 0 || value === 0n);
164
+ }
165
+ }
166
+ throw new XPathError(
167
+ "FORG0006",
168
+ "The sequence has no effective boolean value",
169
+ );
170
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Lexical forms of the numeric types (XSD 1.1 Part 2) and their canonical
3
+ * string representations for casting to xs:string (F&O 3.1 section 19.1.2).
4
+ *
5
+ * Value representations: xs:decimal is a {@link Decimal}, xs:integer and
6
+ * its subtypes are BigInts, xs:double and xs:float are JS numbers (floats
7
+ * rounded with Math.fround; -0, NaN and the infinities are kept).
8
+ *
9
+ * @module @tradik/xslt3/xdm/numeric
10
+ */
11
+
12
+ import { Decimal } from "./decimal.js";
13
+
14
+ const integerPattern = /^[+-]?\d+$/;
15
+ const doublePattern = /^[+-]?(\d+(\.\d*)?|\.\d+)([Ee][+-]?\d+)?$/;
16
+ const specialDoubles = new Map([
17
+ ["INF", Infinity],
18
+ ["+INF", Infinity],
19
+ ["-INF", -Infinity],
20
+ ["NaN", NaN],
21
+ ]);
22
+
23
+ /**
24
+ * Parses an xs:integer lexical form (already whitespace-collapsed).
25
+ * @param {string} text
26
+ * @returns {bigint|null} null when invalid
27
+ */
28
+ export function parseInteger(text) {
29
+ return integerPattern.test(text) ? BigInt(text) : null;
30
+ }
31
+
32
+ /**
33
+ * Parses an xs:decimal lexical form.
34
+ * @param {string} text
35
+ * @returns {Decimal|null} null when invalid
36
+ */
37
+ export function parseDecimal(text) {
38
+ return Decimal.parse(text);
39
+ }
40
+
41
+ /**
42
+ * Parses an xs:double lexical form, including "INF", "+INF", "-INF",
43
+ * "NaN" and negative zero.
44
+ * @param {string} text
45
+ * @returns {number|null} null when invalid
46
+ */
47
+ export function parseDouble(text) {
48
+ if (specialDoubles.has(text)) return specialDoubles.get(text);
49
+ return doublePattern.test(text) ? Number(text) : null;
50
+ }
51
+
52
+ /**
53
+ * Parses an xs:float lexical form. The decimal text is rounded to a double
54
+ * first and then to a float, which can differ from direct rounding in the
55
+ * last bit for rare inputs.
56
+ * @param {string} text
57
+ * @returns {number|null} null when invalid
58
+ */
59
+ export function parseFloat32(text) {
60
+ const value = parseDouble(text);
61
+ return value === null ? null : Math.fround(value);
62
+ }
63
+
64
+ /**
65
+ * Shortest decimal digits that read back as the same double, or as the
66
+ * same float when `isFloat` is set.
67
+ * @param {number} n - Finite, non-zero
68
+ * @param {boolean} isFloat
69
+ * @returns {{digits: string, exponent: number}} significant digits
70
+ * d1d2d3… without trailing zeros; the value is d1.d2d3… × 10^exponent
71
+ */
72
+ function shortestDigits(n, isFloat) {
73
+ let text = n.toExponential();
74
+ for (let precision = 0; isFloat && precision < 9; precision++) {
75
+ const candidate = n.toExponential(precision);
76
+ if (Math.fround(Number(candidate)) === n) {
77
+ text = candidate;
78
+ break;
79
+ }
80
+ }
81
+ const [mantissa, exponent] = text.split("e");
82
+ const digits = mantissa.replace(/^-/, "").replace(".", "").replace(/0+$/, "");
83
+ return { digits, exponent: Number(exponent) };
84
+ }
85
+
86
+ /**
87
+ * Canonical string of an xs:double or xs:float value (F&O 3.1 19.1.2):
88
+ * plain decimal notation for magnitudes in [1e-6, 1e6), otherwise
89
+ * scientific "1.0E10" / "1.5E-7"; "INF", "-INF", "NaN", "0", "-0".
90
+ * @param {number} n
91
+ * @param {boolean} [isFloat=false] - Use the shortest float digits
92
+ * @returns {string}
93
+ */
94
+ export function formatDouble(n, isFloat = false) {
95
+ if (Number.isNaN(n)) return "NaN";
96
+ if (n === Infinity) return "INF";
97
+ if (n === -Infinity) return "-INF";
98
+ if (n === 0) return Object.is(n, -0) ? "-0" : "0";
99
+ const { digits, exponent } = shortestDigits(n, isFloat);
100
+ const sign = n < 0 ? "-" : "";
101
+ const magnitude = Math.abs(n);
102
+ if (magnitude >= 1e-6 && magnitude < 1e6) {
103
+ return (
104
+ sign + Decimal.of(BigInt(digits), digits.length - 1 - exponent).toString()
105
+ );
106
+ }
107
+ return `${sign}${digits[0]}.${digits.slice(1) || "0"}E${exponent}`;
108
+ }
109
+
110
+ /**
111
+ * Converts a finite double or float to the decimal it prints as (the
112
+ * shortest round-trip digits), as used for casting to xs:decimal.
113
+ * @param {number} n - Finite number
114
+ * @param {boolean} [isFloat=false]
115
+ * @returns {Decimal}
116
+ */
117
+ export function numberToDecimal(n, isFloat = false) {
118
+ if (n === 0) return Decimal.ZERO;
119
+ const { digits, exponent } = shortestDigits(n, isFloat);
120
+ const unscaled = BigInt(digits);
121
+ return Decimal.of(n < 0 ? -unscaled : unscaled, digits.length - 1 - exponent);
122
+ }
@@ -0,0 +1,65 @@
1
+ /**
2
+ * xs:QName and xs:NOTATION values: `{ prefix, namespaceURI, localName }`,
3
+ * with "" for no prefix and no namespace. Casting from a string needs a
4
+ * namespace resolver for the prefix.
5
+ *
6
+ * @module @tradik/xslt3/xdm/qname
7
+ */
8
+
9
+ import { XPathError } from "../errors.js";
10
+ import { namePatterns } from "./types.js";
11
+
12
+ /** A qualified name. */
13
+ export class QNameValue {
14
+ /**
15
+ * @param {string} namespaceURI - "" for no namespace
16
+ * @param {string} localName
17
+ * @param {string} [prefix=""]
18
+ */
19
+ constructor(namespaceURI, localName, prefix = "") {
20
+ this.namespaceURI = namespaceURI;
21
+ this.localName = localName;
22
+ this.prefix = prefix;
23
+ Object.freeze(this);
24
+ }
25
+
26
+ /** @returns {string} "prefix:local" or "local" */
27
+ toString() {
28
+ return this.prefix ? `${this.prefix}:${this.localName}` : this.localName;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * Resolves a namespace prefix ("" for the default namespace used for
34
+ * unprefixed names) to a URI, or returns null/undefined when unbound.
35
+ * @callback NamespaceResolver
36
+ * @param {string} prefix
37
+ * @returns {string|null|undefined}
38
+ */
39
+
40
+ /**
41
+ * Parses a lexical QName (whitespace already collapsed).
42
+ * @param {string} text
43
+ * @param {NamespaceResolver} [resolveNamespace] - Without it only
44
+ * unprefixed names can be cast, and they get no namespace
45
+ * @returns {QNameValue|null} null when the text is not a lexical QName
46
+ * @throws {XPathError} FONS0004 when the prefix is not bound
47
+ */
48
+ export function parseQName(text, resolveNamespace) {
49
+ const parts = text.split(":");
50
+ if (
51
+ parts.length > 2 ||
52
+ !parts.every((part) => namePatterns.ncName.test(part))
53
+ ) {
54
+ return null;
55
+ }
56
+ const [prefix, localName] = parts.length === 2 ? parts : ["", parts[0]];
57
+ const namespaceURI = resolveNamespace?.(prefix) ?? (prefix ? null : "");
58
+ if (namespaceURI === null) {
59
+ throw new XPathError(
60
+ "FONS0004",
61
+ `No namespace is bound to the prefix ${prefix}`,
62
+ );
63
+ }
64
+ return new QNameValue(namespaceURI, localName, prefix);
65
+ }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * String helpers of the data model: the whiteSpace facet, the xs:boolean
3
+ * lexical space and codepoint string comparison.
4
+ *
5
+ * @module @tradik/xslt3/xdm/strings
6
+ */
7
+
8
+ /**
9
+ * Applies a whiteSpace facet value (XSD 1.1 Part 2 section 4.3.6). Only the
10
+ * XML whitespace characters (space, tab, CR, LF) are affected.
11
+ * @param {string} text
12
+ * @param {"preserve"|"replace"|"collapse"} mode
13
+ * @returns {string}
14
+ */
15
+ export function normalizeWhitespace(text, mode) {
16
+ if (mode === "preserve") return text;
17
+ const replaced = text.replace(/[\t\n\r]/g, " ");
18
+ // not String#trim, which also strips non-XML whitespace such as U+00A0
19
+ return mode === "replace"
20
+ ? replaced
21
+ : replaced.replace(/ {2,}/g, " ").replace(/^ | $/g, "");
22
+ }
23
+
24
+ /**
25
+ * Parses the xs:boolean lexical forms "true", "false", "1", "0".
26
+ * @param {string} text - Whitespace-collapsed
27
+ * @returns {boolean|null} null when invalid
28
+ */
29
+ export function parseBoolean(text) {
30
+ if (text === "true" || text === "1") return true;
31
+ if (text === "false" || text === "0") return false;
32
+ return null;
33
+ }
34
+
35
+ /**
36
+ * Compares strings by Unicode codepoints (the default collation), unlike
37
+ * the JS operators, which compare UTF-16 code units.
38
+ * @param {string} a
39
+ * @param {string} b
40
+ * @returns {number} -1, 0 or 1
41
+ */
42
+ export function compareCodepoints(a, b) {
43
+ if (a === b) return 0;
44
+ const length = Math.min(a.length, b.length);
45
+ for (let i = 0; i < length; i++) {
46
+ const x = a.codePointAt(i);
47
+ const y = b.codePointAt(i);
48
+ if (x !== y) return x < y ? -1 : 1;
49
+ if (x > 0xffff) i++;
50
+ }
51
+ return a.length < b.length ? -1 : 1;
52
+ }