@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,169 @@
1
+ /**
2
+ * Parser of the XPath regular expression grammar (XSD 1.1 regExp with the
3
+ * F&O 3.1 additions: ^ $ anchors, reluctant quantifiers, back-references,
4
+ * non-capturing groups), producing JS RegExp source for the `u` flag.
5
+ * Character classes and escapes are handled by charClass.js.
6
+ *
7
+ * @module @tradik/xslt3/functions/regex/parser
8
+ */
9
+
10
+ import { parseCharClass } from "./charClass.js";
11
+ import { jsChar, parseEscape, regexError } from "./escapes.js";
12
+ import { RegexReader } from "./reader.js";
13
+
14
+ const isDigit = (c) => c !== undefined && c >= "0" && c <= "9";
15
+
16
+ /** Recursive-descent translator of one pattern to JS source. */
17
+ export class RegexParser {
18
+ /**
19
+ * @param {string} pattern
20
+ * @param {{dotAll: boolean, multiLine: boolean, caseless: boolean}} options
21
+ * - The s and m flags; caseless: categories ignore the i flag
22
+ */
23
+ constructor(pattern, options) {
24
+ this.reader = new RegexReader(pattern, options.caseless);
25
+ this.options = options;
26
+ this.groups = 0;
27
+ this.parents = [0];
28
+ this.open = [];
29
+ this.closed = new Set();
30
+ }
31
+
32
+ /** @returns {string} the translation of the whole pattern */
33
+ translate() {
34
+ const source = this.alternation();
35
+ if (!this.reader.atEnd()) regexError("unmatched )");
36
+ return source;
37
+ }
38
+
39
+ /** @returns {string} branch ("|" branch)* */
40
+ alternation() {
41
+ const branches = [this.branch()];
42
+ while (this.reader.peek() === "|") {
43
+ this.reader.next();
44
+ branches.push(this.branch());
45
+ }
46
+ return branches.join("|");
47
+ }
48
+
49
+ /** @returns {string} a sequence of pieces */
50
+ branch() {
51
+ let source = "";
52
+ for (;;) {
53
+ const c = this.reader.peek();
54
+ if (c === undefined || c === "|" || c === ")") return source;
55
+ source += this.atom() + this.quantifier();
56
+ }
57
+ }
58
+
59
+ /** @returns {string} one atom */
60
+ atom() {
61
+ const { reader, options } = this;
62
+ const c = reader.next();
63
+ switch (c) {
64
+ case "(":
65
+ return this.group();
66
+ case "[":
67
+ return parseCharClass(reader);
68
+ case ".":
69
+ return options.dotAll ? "[^]" : "[^\\n\\r]";
70
+ case "^":
71
+ // a line starts after any \n except one that ends the string
72
+ return options.multiLine ? "(?:^|(?<=\\n)(?!$))" : "(?:^)";
73
+ case "$":
74
+ return options.multiLine ? "(?:$|(?=\\n))" : "(?:$)";
75
+ case "\\":
76
+ return isDigit(reader.peek()) ? this.backReference() : escape(reader);
77
+ default:
78
+ if ("?*+{}]".includes(c)) regexError(`unexpected ${c}`);
79
+ return jsChar(c.codePointAt(0));
80
+ }
81
+ }
82
+
83
+ /** @returns {string} a group, the reader being after "(" */
84
+ group() {
85
+ const { reader } = this;
86
+ let source;
87
+ if (reader.peek() === "?") {
88
+ reader.next();
89
+ if (reader.next() !== ":") regexError("unsupported group syntax (?");
90
+ source = `(?:${this.alternation()})`;
91
+ } else {
92
+ const number = ++this.groups;
93
+ this.parents[number] = this.open.at(-1) ?? 0;
94
+ this.open.push(number);
95
+ source = `(${this.alternation()})`;
96
+ this.open.pop();
97
+ this.closed.add(number);
98
+ }
99
+ if (reader.next() !== ")") regexError("missing )");
100
+ return source;
101
+ }
102
+
103
+ /** @returns {string} a back-reference, the reader being on its digits */
104
+ backReference() {
105
+ const { reader } = this;
106
+ if (reader.peek() === "0") regexError("\\0 is not a back-reference");
107
+ let number = Number(reader.next());
108
+ while (
109
+ isDigit(reader.peek()) &&
110
+ number * 10 + Number(reader.peek()) <= this.groups
111
+ ) {
112
+ number = number * 10 + Number(reader.next());
113
+ }
114
+ if (!this.closed.has(number)) {
115
+ regexError(`back-reference \\${number} to a group not closed before it`);
116
+ }
117
+ return `(?:\\${number})`;
118
+ }
119
+
120
+ /** @returns {string} an optional quantifier with its reluctant mark */
121
+ quantifier() {
122
+ const { reader } = this;
123
+ const c = reader.peek();
124
+ let source;
125
+ if (c === "?" || c === "*" || c === "+") {
126
+ source = reader.next();
127
+ } else if (c === "{") {
128
+ reader.next();
129
+ source = `{${this.bounds()}}`;
130
+ } else {
131
+ return "";
132
+ }
133
+ if (reader.peek() === "?") source += reader.next();
134
+ return source;
135
+ }
136
+
137
+ /** @returns {string} "n", "n," or "n,m" of a {..} quantifier */
138
+ bounds() {
139
+ const { reader } = this;
140
+ const digits = () => {
141
+ let text = "";
142
+ while (isDigit(reader.peek())) text += reader.next();
143
+ return text;
144
+ };
145
+ const min = digits();
146
+ let text = min;
147
+ if (reader.peek() === ",") {
148
+ reader.next();
149
+ const max = digits();
150
+ if (max !== "" && BigInt(max) < BigInt(min || "0")) {
151
+ regexError("quantifier {n,m} with m < n");
152
+ }
153
+ text += `,${max}`;
154
+ }
155
+ if (min === "" || reader.next() !== "}") regexError("invalid quantifier");
156
+ return text;
157
+ }
158
+ }
159
+
160
+ /**
161
+ * An escape outside a class as a JS atom.
162
+ * @param {RegexReader} reader
163
+ * @returns {string}
164
+ */
165
+ function escape(reader) {
166
+ const item = parseEscape(reader);
167
+ if (item.char !== undefined) return item.set;
168
+ return item.atom ?? `[${item.set}]`;
169
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A cursor over the codepoints of a regular expression.
3
+ *
4
+ * @module @tradik/xslt3/functions/regex/reader
5
+ */
6
+
7
+ /** Reads a pattern codepoint by codepoint. */
8
+ export class RegexReader {
9
+ /**
10
+ * @param {string} pattern
11
+ * @param {boolean} [caseless=false] - Whether the i flag is set and JS
12
+ * supports the (?-i:...) modifier, so categories can ignore it
13
+ */
14
+ constructor(pattern, caseless = false) {
15
+ this.chars = Array.from(pattern);
16
+ this.pos = 0;
17
+ this.caseless = caseless;
18
+ }
19
+
20
+ /**
21
+ * @param {number} [offset=0]
22
+ * @returns {string|undefined} the character at the cursor plus offset
23
+ */
24
+ peek(offset = 0) {
25
+ return this.chars[this.pos + offset];
26
+ }
27
+
28
+ /** @returns {string|undefined} the character at the cursor, consumed */
29
+ next() {
30
+ return this.chars[this.pos++];
31
+ }
32
+
33
+ /** @returns {boolean} whether the whole pattern has been read */
34
+ atEnd() {
35
+ return this.pos >= this.chars.length;
36
+ }
37
+ }
@@ -0,0 +1,140 @@
1
+ /**
2
+ * fn:matches, fn:replace and fn:tokenize (F&O 3.1 sections 5.6.3 to
3
+ * 5.6.5).
4
+ *
5
+ * @module @tradik/xslt3/functions/regex/regexFunctions
6
+ */
7
+
8
+ import { XPathError } from "../../errors.js";
9
+ import { normalizeWhitespace } from "../../xdm/strings.js";
10
+ import { booleanItem, define, stringArg, stringItem } from "../support.js";
11
+ import {
12
+ allMatches,
13
+ compileRegex,
14
+ rejectEmptyMatch,
15
+ testRegex,
16
+ } from "./compile.js";
17
+
18
+ /** @param {Array<*>|undefined} flags @returns {string} the flags argument */
19
+ const flagsArg = (flags) => (flags === undefined ? "" : flags[0].value);
20
+
21
+ /**
22
+ * Parses a replacement string into literal text and group references.
23
+ * @param {string} replacement
24
+ * @param {number} groups - Number of capturing groups of the pattern
25
+ * @returns {Array<string|number>} literal strings and group numbers
26
+ * @throws {XPathError} FORX0004 for a "\" or "$" not used as an escape or
27
+ * a group reference
28
+ */
29
+ export function parseReplacement(replacement, groups) {
30
+ const parts = [];
31
+ const isDigit = (c) => c >= "0" && c <= "9";
32
+ for (let i = 0; i < replacement.length; i++) {
33
+ const c = replacement[i];
34
+ const next = replacement[i + 1] ?? "";
35
+ if (c === "\\" && (next === "\\" || next === "$")) {
36
+ parts.push(next);
37
+ i++;
38
+ } else if (c === "$" && isDigit(next)) {
39
+ let number = Number(next);
40
+ i++;
41
+ while (
42
+ isDigit(replacement[i + 1] ?? "") &&
43
+ number * 10 + Number(replacement[i + 1]) <= groups
44
+ ) {
45
+ number = number * 10 + Number(replacement[++i]);
46
+ }
47
+ parts.push(number);
48
+ } else if (c === "\\" || c === "$") {
49
+ throw new XPathError(
50
+ "FORX0004",
51
+ `Invalid replacement string: ${replacement}`,
52
+ );
53
+ } else {
54
+ parts.push(c);
55
+ }
56
+ }
57
+ return parts;
58
+ }
59
+
60
+ /**
61
+ * fn:replace on strings.
62
+ * @param {string} input
63
+ * @param {string} pattern
64
+ * @param {string} replacement
65
+ * @param {string} [flags=""]
66
+ * @returns {string}
67
+ */
68
+ export function replace(input, pattern, replacement, flags = "") {
69
+ const compiled = compileRegex(pattern, flags);
70
+ const parts = compiled.literal
71
+ ? [replacement]
72
+ : parseReplacement(replacement, compiled.groups);
73
+ rejectEmptyMatch(compiled);
74
+ let result = "";
75
+ let last = 0;
76
+ for (const match of allMatches(compiled, input)) {
77
+ result += input.slice(last, match.index);
78
+ for (const part of parts) {
79
+ result += typeof part === "number" ? (match[part] ?? "") : part;
80
+ }
81
+ last = match.index + match[0].length;
82
+ }
83
+ return result + input.slice(last);
84
+ }
85
+
86
+ /**
87
+ * fn:tokenize on strings (the two- and three-argument forms).
88
+ * @param {string} input
89
+ * @param {string} pattern
90
+ * @param {string} [flags=""]
91
+ * @returns {string[]}
92
+ */
93
+ export function tokenize(input, pattern, flags = "") {
94
+ const compiled = compileRegex(pattern, flags);
95
+ rejectEmptyMatch(compiled);
96
+ if (input === "") return [];
97
+ const tokens = [];
98
+ let last = 0;
99
+ for (const match of allMatches(compiled, input)) {
100
+ tokens.push(input.slice(last, match.index));
101
+ last = match.index + match[0].length;
102
+ }
103
+ tokens.push(input.slice(last));
104
+ return tokens;
105
+ }
106
+
107
+ const matchesImpl = ([input, [pattern], flags]) => [
108
+ booleanItem(
109
+ testRegex(compileRegex(pattern.value, flagsArg(flags)), stringArg(input)),
110
+ ),
111
+ ];
112
+ const replaceImpl = ([input, [pattern], [replacement], flags]) => [
113
+ stringItem(
114
+ replace(
115
+ stringArg(input),
116
+ pattern.value,
117
+ replacement.value,
118
+ flagsArg(flags),
119
+ ),
120
+ ),
121
+ ];
122
+ const tokenizeImpl = ([input, [pattern], flags]) =>
123
+ tokenize(stringArg(input), pattern.value, flagsArg(flags)).map(stringItem);
124
+
125
+ const S = "xs:string";
126
+ const S_OPT = "xs:string?";
127
+
128
+ /** @type {import("../support.js").FunctionDefinition[]} */
129
+ export const regexFunctions = [
130
+ define("matches", [S_OPT, S], "xs:boolean", matchesImpl),
131
+ define("matches", [S_OPT, S, S], "xs:boolean", matchesImpl),
132
+ define("replace", [S_OPT, S, S], S, replaceImpl),
133
+ define("replace", [S_OPT, S, S, S], S, replaceImpl),
134
+ define("tokenize", [S_OPT], "xs:string*", ([input]) => {
135
+ const text = normalizeWhitespace(stringArg(input), "collapse");
136
+ return text === "" ? [] : text.split(" ").map(stringItem);
137
+ }),
138
+ define("tokenize", [S_OPT, S], "xs:string*", tokenizeImpl),
139
+ define("tokenize", [S_OPT, S, S], "xs:string*", tokenizeImpl),
140
+ ];
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Translator of XPath 3.1 regular expressions (F&O 3.1 section 5.6.1:
3
+ * XSD 1.1 regular expressions plus ^ and $ anchors, reluctant quantifiers,
4
+ * back-references and non-capturing groups) and their flags (s, m, i, x,
5
+ * q) into JavaScript RegExp source for the `u` flag.
6
+ *
7
+ * Semantics kept from XPath rather than JS: "." excludes only \n and \r
8
+ * (everything with the s flag), multi-line ^ and $ only see \n as a line
9
+ * end, \d \w \s \i \c follow XSD, and lookaround, \b, \x and other JS-only
10
+ * syntax are rejected with FORX0002. JS's own `s` and `m` flags are never
11
+ * used; their effects are written into the expression.
12
+ *
13
+ * @module @tradik/xslt3/functions/regex/translate
14
+ */
15
+
16
+ import { XPathError } from "../../errors.js";
17
+ import { jsChar } from "./escapes.js";
18
+ import { RegexParser } from "./parser.js";
19
+
20
+ /**
21
+ * @typedef {object} TranslatedRegex
22
+ * @property {string} source - JS RegExp source
23
+ * @property {string} flags - JS flags: "u" or "iu"
24
+ * @property {number} groups - Number of capturing groups
25
+ * @property {number[]} parents - Enclosing group of each group (index =
26
+ * group number, 0 for the whole match)
27
+ */
28
+
29
+ /** Whether JS supports the (?-i:...) modifier (V8 12.5, Node 23). */
30
+ export const MODIFIERS = (() => {
31
+ try {
32
+ return new RegExp("(?-i:a)", "iu").test("a");
33
+ /* node:coverage ignore next 3 */
34
+ } catch {
35
+ return false;
36
+ }
37
+ })();
38
+
39
+ const XML_SPACE = new Set([" ", "\t", "\n", "\r"]);
40
+
41
+ /**
42
+ * Removes whitespace outside character classes (the x flag).
43
+ * @param {string} pattern
44
+ * @returns {string}
45
+ */
46
+ export function stripWhitespace(pattern) {
47
+ let result = "";
48
+ let depth = 0;
49
+ const chars = Array.from(pattern);
50
+ for (let i = 0; i < chars.length; i++) {
51
+ const c = chars[i];
52
+ if (c === "\\") {
53
+ // the escaped character is the next one after removed whitespace
54
+ if (depth === 0) {
55
+ while (XML_SPACE.has(chars[i + 1])) i++;
56
+ }
57
+ result += c + (chars[++i] ?? "");
58
+ continue;
59
+ }
60
+ if (c === "[") depth++;
61
+ else if (c === "]" && depth > 0) depth--;
62
+ if (depth > 0 || !XML_SPACE.has(c)) result += c;
63
+ }
64
+ return result;
65
+ }
66
+
67
+ /**
68
+ * Translates an XPath regular expression and its flags.
69
+ * @param {string} pattern
70
+ * @param {string} [flags=""]
71
+ * @returns {TranslatedRegex}
72
+ * @throws {XPathError} FORX0001 for invalid flags, FORX0002 for an invalid
73
+ * pattern
74
+ */
75
+ export function translateRegex(pattern, flags = "") {
76
+ if (!/^[smixq]*$/.test(flags)) {
77
+ throw new XPathError(
78
+ "FORX0001",
79
+ `Invalid regular expression flags "${flags}"`,
80
+ );
81
+ }
82
+ const jsFlags = flags.includes("i") ? "iu" : "u";
83
+ if (flags.includes("q")) {
84
+ const source = Array.from(pattern, (c) => jsChar(c.codePointAt(0))).join(
85
+ "",
86
+ );
87
+ return { source, flags: jsFlags, groups: 0, parents: [0] };
88
+ }
89
+ const text = flags.includes("x") ? stripWhitespace(pattern) : pattern;
90
+ const translator = new RegexParser(text, {
91
+ dotAll: flags.includes("s"),
92
+ multiLine: flags.includes("m"),
93
+ caseless: flags.includes("i") && MODIFIERS,
94
+ });
95
+ const source = translator.translate();
96
+ return {
97
+ source,
98
+ flags: jsFlags,
99
+ groups: translator.groups,
100
+ parents: translator.parents,
101
+ };
102
+ }
@@ -0,0 +1,194 @@
1
+ /**
2
+ * Unicode block ranges for the regular expression escapes \p{IsBlock}
3
+ * (XSD 1.1 Part 2 appendix G.4.2.3), generated from Blocks.txt of
4
+ * Unicode 15.0. Names have spaces and underscores removed; hyphens are
5
+ * kept ("IsLatin-1Supplement").
6
+ *
7
+ * @module @tradik/xslt3/functions/regex/unicodeBlocks
8
+ */
9
+
10
+ /** "Name:start-end" entries (hex), separated by semicolons. */
11
+ const TABLE =
12
+ "BasicLatin:0-7f;Latin-1Supplement:80-ff;LatinExtended-A:100-17f;" +
13
+ "LatinExtended-B:180-24f;IPAExtensions:250-2af;" +
14
+ "SpacingModifierLetters:2b0-2ff;CombiningDiacriticalMarks:300-36f;" +
15
+ "GreekandCoptic:370-3ff;Cyrillic:400-4ff;CyrillicSupplement:500-52f;" +
16
+ "Armenian:530-58f;Hebrew:590-5ff;Arabic:600-6ff;Syriac:700-74f;" +
17
+ "ArabicSupplement:750-77f;Thaana:780-7bf;NKo:7c0-7ff;Samaritan:800-83f;" +
18
+ "Mandaic:840-85f;SyriacSupplement:860-86f;ArabicExtended-B:870-89f;" +
19
+ "ArabicExtended-A:8a0-8ff;Devanagari:900-97f;Bengali:980-9ff;" +
20
+ "Gurmukhi:a00-a7f;Gujarati:a80-aff;Oriya:b00-b7f;Tamil:b80-bff;" +
21
+ "Telugu:c00-c7f;Kannada:c80-cff;Malayalam:d00-d7f;Sinhala:d80-dff;" +
22
+ "Thai:e00-e7f;Lao:e80-eff;Tibetan:f00-fff;Myanmar:1000-109f;" +
23
+ "Georgian:10a0-10ff;HangulJamo:1100-11ff;Ethiopic:1200-137f;" +
24
+ "EthiopicSupplement:1380-139f;Cherokee:13a0-13ff;" +
25
+ "UnifiedCanadianAboriginalSyllabics:1400-167f;Ogham:1680-169f;" +
26
+ "Runic:16a0-16ff;Tagalog:1700-171f;Hanunoo:1720-173f;Buhid:1740-175f;" +
27
+ "Tagbanwa:1760-177f;Khmer:1780-17ff;Mongolian:1800-18af;" +
28
+ "UnifiedCanadianAboriginalSyllabicsExtended:18b0-18ff;Limbu:1900-194f;" +
29
+ "TaiLe:1950-197f;NewTaiLue:1980-19df;KhmerSymbols:19e0-19ff;" +
30
+ "Buginese:1a00-1a1f;TaiTham:1a20-1aaf;" +
31
+ "CombiningDiacriticalMarksExtended:1ab0-1aff;Balinese:1b00-1b7f;" +
32
+ "Sundanese:1b80-1bbf;Batak:1bc0-1bff;Lepcha:1c00-1c4f;OlChiki:1c50-1c7f;" +
33
+ "CyrillicExtended-C:1c80-1c8f;GeorgianExtended:1c90-1cbf;" +
34
+ "SundaneseSupplement:1cc0-1ccf;VedicExtensions:1cd0-1cff;" +
35
+ "PhoneticExtensions:1d00-1d7f;PhoneticExtensionsSupplement:1d80-1dbf;" +
36
+ "CombiningDiacriticalMarksSupplement:1dc0-1dff;" +
37
+ "LatinExtendedAdditional:1e00-1eff;GreekExtended:1f00-1fff;" +
38
+ "GeneralPunctuation:2000-206f;SuperscriptsandSubscripts:2070-209f;" +
39
+ "CurrencySymbols:20a0-20cf;CombiningDiacriticalMarksforSymbols:20d0-20ff;" +
40
+ "LetterlikeSymbols:2100-214f;NumberForms:2150-218f;Arrows:2190-21ff;" +
41
+ "MathematicalOperators:2200-22ff;MiscellaneousTechnical:2300-23ff;" +
42
+ "ControlPictures:2400-243f;OpticalCharacterRecognition:2440-245f;" +
43
+ "EnclosedAlphanumerics:2460-24ff;BoxDrawing:2500-257f;" +
44
+ "BlockElements:2580-259f;GeometricShapes:25a0-25ff;" +
45
+ "MiscellaneousSymbols:2600-26ff;Dingbats:2700-27bf;" +
46
+ "MiscellaneousMathematicalSymbols-A:27c0-27ef;" +
47
+ "SupplementalArrows-A:27f0-27ff;BraillePatterns:2800-28ff;" +
48
+ "SupplementalArrows-B:2900-297f;" +
49
+ "MiscellaneousMathematicalSymbols-B:2980-29ff;" +
50
+ "SupplementalMathematicalOperators:2a00-2aff;" +
51
+ "MiscellaneousSymbolsandArrows:2b00-2bff;Glagolitic:2c00-2c5f;" +
52
+ "LatinExtended-C:2c60-2c7f;Coptic:2c80-2cff;GeorgianSupplement:2d00-2d2f;" +
53
+ "Tifinagh:2d30-2d7f;EthiopicExtended:2d80-2ddf;CyrillicExtended-A:2de0-2dff;" +
54
+ "SupplementalPunctuation:2e00-2e7f;CJKRadicalsSupplement:2e80-2eff;" +
55
+ "KangxiRadicals:2f00-2fdf;IdeographicDescriptionCharacters:2ff0-2fff;" +
56
+ "CJKSymbolsandPunctuation:3000-303f;Hiragana:3040-309f;Katakana:30a0-30ff;" +
57
+ "Bopomofo:3100-312f;HangulCompatibilityJamo:3130-318f;Kanbun:3190-319f;" +
58
+ "BopomofoExtended:31a0-31bf;CJKStrokes:31c0-31ef;" +
59
+ "KatakanaPhoneticExtensions:31f0-31ff;EnclosedCJKLettersandMonths:3200-32ff;" +
60
+ "CJKCompatibility:3300-33ff;CJKUnifiedIdeographsExtensionA:3400-4dbf;" +
61
+ "YijingHexagramSymbols:4dc0-4dff;CJKUnifiedIdeographs:4e00-9fff;" +
62
+ "YiSyllables:a000-a48f;YiRadicals:a490-a4cf;Lisu:a4d0-a4ff;Vai:a500-a63f;" +
63
+ "CyrillicExtended-B:a640-a69f;Bamum:a6a0-a6ff;ModifierToneLetters:a700-a71f;" +
64
+ "LatinExtended-D:a720-a7ff;SylotiNagri:a800-a82f;" +
65
+ "CommonIndicNumberForms:a830-a83f;Phags-pa:a840-a87f;Saurashtra:a880-a8df;" +
66
+ "DevanagariExtended:a8e0-a8ff;KayahLi:a900-a92f;Rejang:a930-a95f;" +
67
+ "HangulJamoExtended-A:a960-a97f;Javanese:a980-a9df;" +
68
+ "MyanmarExtended-B:a9e0-a9ff;Cham:aa00-aa5f;MyanmarExtended-A:aa60-aa7f;" +
69
+ "TaiViet:aa80-aadf;MeeteiMayekExtensions:aae0-aaff;" +
70
+ "EthiopicExtended-A:ab00-ab2f;LatinExtended-E:ab30-ab6f;" +
71
+ "CherokeeSupplement:ab70-abbf;MeeteiMayek:abc0-abff;" +
72
+ "HangulSyllables:ac00-d7af;HangulJamoExtended-B:d7b0-d7ff;" +
73
+ "HighSurrogates:d800-db7f;HighPrivateUseSurrogates:db80-dbff;" +
74
+ "LowSurrogates:dc00-dfff;PrivateUseArea:e000-f8ff;" +
75
+ "CJKCompatibilityIdeographs:f900-faff;AlphabeticPresentationForms:fb00-fb4f;" +
76
+ "ArabicPresentationForms-A:fb50-fdff;VariationSelectors:fe00-fe0f;" +
77
+ "VerticalForms:fe10-fe1f;CombiningHalfMarks:fe20-fe2f;" +
78
+ "CJKCompatibilityForms:fe30-fe4f;SmallFormVariants:fe50-fe6f;" +
79
+ "ArabicPresentationForms-B:fe70-feff;HalfwidthandFullwidthForms:ff00-ffef;" +
80
+ "Specials:fff0-ffff;LinearBSyllabary:10000-1007f;" +
81
+ "LinearBIdeograms:10080-100ff;AegeanNumbers:10100-1013f;" +
82
+ "AncientGreekNumbers:10140-1018f;AncientSymbols:10190-101cf;" +
83
+ "PhaistosDisc:101d0-101ff;Lycian:10280-1029f;Carian:102a0-102df;" +
84
+ "CopticEpactNumbers:102e0-102ff;OldItalic:10300-1032f;Gothic:10330-1034f;" +
85
+ "OldPermic:10350-1037f;Ugaritic:10380-1039f;OldPersian:103a0-103df;" +
86
+ "Deseret:10400-1044f;Shavian:10450-1047f;Osmanya:10480-104af;" +
87
+ "Osage:104b0-104ff;Elbasan:10500-1052f;CaucasianAlbanian:10530-1056f;" +
88
+ "Vithkuqi:10570-105bf;LinearA:10600-1077f;LatinExtended-F:10780-107bf;" +
89
+ "CypriotSyllabary:10800-1083f;ImperialAramaic:10840-1085f;" +
90
+ "Palmyrene:10860-1087f;Nabataean:10880-108af;Hatran:108e0-108ff;" +
91
+ "Phoenician:10900-1091f;Lydian:10920-1093f;MeroiticHieroglyphs:10980-1099f;" +
92
+ "MeroiticCursive:109a0-109ff;Kharoshthi:10a00-10a5f;" +
93
+ "OldSouthArabian:10a60-10a7f;OldNorthArabian:10a80-10a9f;" +
94
+ "Manichaean:10ac0-10aff;Avestan:10b00-10b3f;" +
95
+ "InscriptionalParthian:10b40-10b5f;InscriptionalPahlavi:10b60-10b7f;" +
96
+ "PsalterPahlavi:10b80-10baf;OldTurkic:10c00-10c4f;OldHungarian:10c80-10cff;" +
97
+ "HanifiRohingya:10d00-10d3f;RumiNumeralSymbols:10e60-10e7f;" +
98
+ "Yezidi:10e80-10ebf;ArabicExtended-C:10ec0-10eff;OldSogdian:10f00-10f2f;" +
99
+ "Sogdian:10f30-10f6f;OldUyghur:10f70-10faf;Chorasmian:10fb0-10fdf;" +
100
+ "Elymaic:10fe0-10fff;Brahmi:11000-1107f;Kaithi:11080-110cf;" +
101
+ "SoraSompeng:110d0-110ff;Chakma:11100-1114f;Mahajani:11150-1117f;" +
102
+ "Sharada:11180-111df;SinhalaArchaicNumbers:111e0-111ff;Khojki:11200-1124f;" +
103
+ "Multani:11280-112af;Khudawadi:112b0-112ff;Grantha:11300-1137f;" +
104
+ "Newa:11400-1147f;Tirhuta:11480-114df;Siddham:11580-115ff;Modi:11600-1165f;" +
105
+ "MongolianSupplement:11660-1167f;Takri:11680-116cf;Ahom:11700-1174f;" +
106
+ "Dogra:11800-1184f;WarangCiti:118a0-118ff;DivesAkuru:11900-1195f;" +
107
+ "Nandinagari:119a0-119ff;ZanabazarSquare:11a00-11a4f;Soyombo:11a50-11aaf;" +
108
+ "UnifiedCanadianAboriginalSyllabicsExtended-A:11ab0-11abf;" +
109
+ "PauCinHau:11ac0-11aff;DevanagariExtended-A:11b00-11b5f;" +
110
+ "Bhaiksuki:11c00-11c6f;Marchen:11c70-11cbf;MasaramGondi:11d00-11d5f;" +
111
+ "GunjalaGondi:11d60-11daf;Makasar:11ee0-11eff;Kawi:11f00-11f5f;" +
112
+ "LisuSupplement:11fb0-11fbf;TamilSupplement:11fc0-11fff;" +
113
+ "Cuneiform:12000-123ff;CuneiformNumbersandPunctuation:12400-1247f;" +
114
+ "EarlyDynasticCuneiform:12480-1254f;Cypro-Minoan:12f90-12fff;" +
115
+ "EgyptianHieroglyphs:13000-1342f;" +
116
+ "EgyptianHieroglyphFormatControls:13430-1345f;" +
117
+ "AnatolianHieroglyphs:14400-1467f;BamumSupplement:16800-16a3f;" +
118
+ "Mro:16a40-16a6f;Tangsa:16a70-16acf;BassaVah:16ad0-16aff;" +
119
+ "PahawhHmong:16b00-16b8f;Medefaidrin:16e40-16e9f;Miao:16f00-16f9f;" +
120
+ "IdeographicSymbolsandPunctuation:16fe0-16fff;Tangut:17000-187ff;" +
121
+ "TangutComponents:18800-18aff;KhitanSmallScript:18b00-18cff;" +
122
+ "TangutSupplement:18d00-18d7f;KanaExtended-B:1aff0-1afff;" +
123
+ "KanaSupplement:1b000-1b0ff;KanaExtended-A:1b100-1b12f;" +
124
+ "SmallKanaExtension:1b130-1b16f;Nushu:1b170-1b2ff;Duployan:1bc00-1bc9f;" +
125
+ "ShorthandFormatControls:1bca0-1bcaf;ZnamennyMusicalNotation:1cf00-1cfcf;" +
126
+ "ByzantineMusicalSymbols:1d000-1d0ff;MusicalSymbols:1d100-1d1ff;" +
127
+ "AncientGreekMusicalNotation:1d200-1d24f;KaktovikNumerals:1d2c0-1d2df;" +
128
+ "MayanNumerals:1d2e0-1d2ff;TaiXuanJingSymbols:1d300-1d35f;" +
129
+ "CountingRodNumerals:1d360-1d37f;" +
130
+ "MathematicalAlphanumericSymbols:1d400-1d7ff;SuttonSignWriting:1d800-1daaf;" +
131
+ "LatinExtended-G:1df00-1dfff;GlagoliticSupplement:1e000-1e02f;" +
132
+ "CyrillicExtended-D:1e030-1e08f;NyiakengPuachueHmong:1e100-1e14f;" +
133
+ "Toto:1e290-1e2bf;Wancho:1e2c0-1e2ff;NagMundari:1e4d0-1e4ff;" +
134
+ "EthiopicExtended-B:1e7e0-1e7ff;MendeKikakui:1e800-1e8df;Adlam:1e900-1e95f;" +
135
+ "IndicSiyaqNumbers:1ec70-1ecbf;OttomanSiyaqNumbers:1ed00-1ed4f;" +
136
+ "ArabicMathematicalAlphabeticSymbols:1ee00-1eeff;MahjongTiles:1f000-1f02f;" +
137
+ "DominoTiles:1f030-1f09f;PlayingCards:1f0a0-1f0ff;" +
138
+ "EnclosedAlphanumericSupplement:1f100-1f1ff;" +
139
+ "EnclosedIdeographicSupplement:1f200-1f2ff;" +
140
+ "MiscellaneousSymbolsandPictographs:1f300-1f5ff;Emoticons:1f600-1f64f;" +
141
+ "OrnamentalDingbats:1f650-1f67f;TransportandMapSymbols:1f680-1f6ff;" +
142
+ "AlchemicalSymbols:1f700-1f77f;GeometricShapesExtended:1f780-1f7ff;" +
143
+ "SupplementalArrows-C:1f800-1f8ff;" +
144
+ "SupplementalSymbolsandPictographs:1f900-1f9ff;ChessSymbols:1fa00-1fa6f;" +
145
+ "SymbolsandPictographsExtended-A:1fa70-1faff;" +
146
+ "SymbolsforLegacyComputing:1fb00-1fbff;" +
147
+ "CJKUnifiedIdeographsExtensionB:20000-2a6df;" +
148
+ "CJKUnifiedIdeographsExtensionC:2a700-2b73f;" +
149
+ "CJKUnifiedIdeographsExtensionD:2b740-2b81f;" +
150
+ "CJKUnifiedIdeographsExtensionE:2b820-2ceaf;" +
151
+ "CJKUnifiedIdeographsExtensionF:2ceb0-2ebef;" +
152
+ "CJKCompatibilityIdeographsSupplement:2f800-2fa1f;" +
153
+ "CJKUnifiedIdeographsExtensionG:30000-3134f;" +
154
+ "CJKUnifiedIdeographsExtensionH:31350-323af;Tags:e0000-e007f;" +
155
+ "VariationSelectorsSupplement:e0100-e01ef;" +
156
+ "SupplementaryPrivateUseArea-A:f0000-fffff;" +
157
+ "SupplementaryPrivateUseArea-B:100000-10ffff";
158
+
159
+ /**
160
+ * Former block names that XSD 1.0 regular expressions use, mapped to the
161
+ * current names.
162
+ */
163
+ const ALIASES = {
164
+ Greek: "GreekandCoptic",
165
+ CombiningMarksforSymbols: "CombiningDiacriticalMarksforSymbols",
166
+ CyrillicSupplementary: "CyrillicSupplement",
167
+ };
168
+
169
+ /** @type {Map<string, [number, number][]>} ranges by block name */
170
+ const blocks = new Map();
171
+ for (const entry of TABLE.split(";")) {
172
+ const [name, range] = entry.split(":");
173
+ const [start, end] = range.split("-").map((hex) => parseInt(hex, 16));
174
+ blocks.set(name, [[start, end]]);
175
+ }
176
+ for (const [alias, name] of Object.entries(ALIASES)) {
177
+ blocks.set(alias, blocks.get(name));
178
+ }
179
+ // XSD 1.0 "PrivateUse" also covers the supplementary private use planes
180
+ blocks.set("PrivateUse", [
181
+ ...blocks.get("PrivateUseArea"),
182
+ ...blocks.get("SupplementaryPrivateUseArea-A"),
183
+ ...blocks.get("SupplementaryPrivateUseArea-B"),
184
+ ]);
185
+
186
+ /**
187
+ * Ranges of a Unicode block (the name is case-sensitive; spaces and
188
+ * underscores are ignored).
189
+ * @param {string} name - Block name without the "Is" prefix
190
+ * @returns {[number, number][]|undefined} inclusive codepoint ranges
191
+ */
192
+ export function blockRanges(name) {
193
+ return blocks.get(name.replace(/[\s_]/g, ""));
194
+ }