]> Lady’s Gitweb - Sutra/blob - xsd/functions.js
Add all X·S·D non‐auxillary functions
[Sutra] / xsd / functions.js
1 // ♓️🪡 सूत्र ∷ xsd/functions.js
2 // ====================================================================
3 //
4 // Copyright © 2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import {
11 abs,
12 bind,
13 call,
14 get8BitUnsignedIntegralItem,
15 getByteLength,
16 getFirstSubstringIndex,
17 getPrototype,
18 isArrayBuffer,
19 isFiniteNumber,
20 isIntegralNumber,
21 ITERATOR,
22 Matcher,
23 min,
24 objectCreate,
25 rawString,
26 sameValue,
27 set8BitIntegralItem,
28 stringCatenate,
29 stringIncludes,
30 stringPadEnd,
31 stringPadStart,
32 stringSlice,
33 stringSplit,
34 substring,
35 toExponentialNotation,
36 toFloat32,
37 toNumber,
38 type,
39 } from "../deps.js";
40 import { div, mod } from "./operators.js";
41 import {
42 booleanRep,
43 dateLexicalRep,
44 dateTimeLexicalRep,
45 dayFrag,
46 dayTimeDurationLexicalRep,
47 decimalLexicalRep,
48 decimalPtNumeral,
49 digit as lexicalDigit,
50 doubleRep,
51 duDayFrag,
52 duDayTimeFrag,
53 duHourFrag,
54 duMinuteFrag,
55 duMonthFrag,
56 durationLexicalRep,
57 duSecondFrag,
58 duTimeFrag,
59 duYearFrag,
60 duYearMonthFrag,
61 floatRep,
62 fracFrag,
63 gDayLexicalRep,
64 gMonthDayLexicalRep,
65 gMonthLexicalRep,
66 gYearLexicalRep,
67 gYearMonthLexicalRep,
68 hexBinary,
69 hexDigit,
70 hexOctet,
71 hourFrag,
72 minuteFrag,
73 monthFrag,
74 noDecimalPtNumeral,
75 numericalSpecialRep,
76 scientificNotationNumeral,
77 secondFrag,
78 stringRep,
79 timeLexicalRep,
80 timezoneFrag,
81 unsignedDecimalPtNumeral,
82 unsignedNoDecimalPtNumeral,
83 yearFrag,
84 yearMonthDurationLexicalRep,
85 } from "./productions.js";
86 import {
87 absent,
88 negativeInfinity,
89 negativeZero,
90 notANumber,
91 positiveInfinity,
92 positiveZero,
93 } from "./values.js";
94
95 const stringToFloat = parseFloat;
96
97 /**
98 * Ensures that the provided matcher matches the provided value and
99 * returns it.
100 *
101 * ☡ This function throws if the matcher does not match.
102 *
103 * ※ This function is not exposed.
104 */
105 const ensureMatches = (matcher, $) => {
106 if (matcher($)) {
107 return $;
108 } else {
109 throw new TypeError(
110 `सूत्र: Expected a ${matcher.name}, but got: ${$}.`,
111 );
112 }
113 };
114
115 /**
116 * Ensures that the provided value with the provided name is present or
117 * absent.
118 *
119 * ☡ This function throws if the presence of the value is not correct.
120 *
121 * ※ This function is not exposed.
122 */
123 const ensurePresence = (name, value, present = true) => {
124 if ((value !== absent) === !present) {
125 throw new TypeError(
126 `सूत्र: Expected ${name} to be ${
127 present ? "present" : "absent"
128 }, but got: ${value}.`,
129 );
130 } else {
131 return value;
132 }
133 };
134
135 /**
136 * Ensures that the provided value is a valid, complete duration and
137 * returns an object with its months and seconds.
138 *
139 * ☡ This function throws if the provided value is not a valid,
140 * complete duration.
141 *
142 * ※ This function is not exposed.
143 */
144 const duration = ($) => {
145 const { months, seconds } = $;
146 if (!isIntegralNumber(months)) {
147 throw new TypeError(
148 `सूत्र: Expected duration to have an integer for months, but got: ${months}.`,
149 );
150 } else if (!isFiniteNumber(seconds)) {
151 throw new TypeError(
152 `सूत्र: Expected duration to have a finite number for seconds, but got: ${seconds}.`,
153 );
154 } else if (months > 0 && seconds < 0 || months < 0 && seconds > 0) {
155 throw new TypeError(
156 `सूत्र: Expected seconds in duration to match polarity of months, but got: ${seconds}.`,
157 );
158 } else {
159 return { months, seconds };
160 }
161 };
162
163 /**
164 * Ensures that the provided value is a valid
165 * date/timeSevenPropertyModel value and returns an object with its
166 * year, month, day, hour, minute, second, and timezoneOffset.
167 *
168 * ☡ This function throws if the provided value is not a valid
169 * date/timeSevenPropertyModel value.
170 *
171 * ※ This function is not exposed.
172 */
173 const date·timeSevenPropertyModel = ($) => {
174 if (type($) !== "object") {
175 throw new TypeError(
176 `सूत्र: Expected a date/timeSevenPropertyModel value, but got: ${$}.`,
177 );
178 } else {
179 const { year, month, day, hour, minute, second, timezoneOffset } =
180 $;
181 if (year !== absent && !isIntegralNumber(year)) {
182 throw new TypeError(
183 `सूत्र: Expected year to be an optional integer, but got: ${year}.`,
184 );
185 } else if (
186 month !== absent &&
187 !(isIntegralNumber(month) && month >= 1 && month <= 12)
188 ) {
189 throw new TypeError(
190 `सूत्र: Expected month to be an optional integer between 1 and 12 inclusive, but got: ${month}.`,
191 );
192 } else if (
193 day !== absent &&
194 !(isIntegralNumber(day) && day >= 1 && day <= 31)
195 ) {
196 throw new TypeError(
197 `सूत्र: Expected day to be an optional integer between 1 and 31 inclusive, but got: ${day}.`,
198 );
199 } else if (
200 hour !== absent &&
201 !(isIntegralNumber(hour) && hour >= 0 && hour <= 24)
202 ) {
203 throw new TypeError(
204 `सूत्र: Expected hour to be an optional integer between 0 and 24 inclusive, but got: ${hour}.`,
205 );
206 } else if (
207 minute !== absent &&
208 !(isIntegralNumber(minute) && minute >= 0 && minute <= 59)
209 ) {
210 throw new TypeError(
211 `सूत्र: Expected minute to be an optional integer between 0 and 59 inclusive, but got: ${minute}.`,
212 );
213 } else if (
214 second !== absent &&
215 !(isFiniteNumber(second) && second >= 0 && second < 60)
216 ) {
217 throw new TypeError(
218 `सूत्र: Expected second to be an optional finite number greater than or equal to 0 and less than 60, but got: ${second}.`,
219 );
220 } else if (
221 timezoneOffset !== absent &&
222 !(isIntegralNumber(timezoneOffset) && timezoneOffset >= -840 &&
223 timezoneOffset <= 840)
224 ) {
225 throw new TypeError(
226 `सूत्र: Expected timezoneOffset to be an optional integer between -840 and 840 inclusive, but got: ${timezoneOffset}.`,
227 );
228 } else if (
229 month !== absent && day !== absent && !(
230 month === 2 && day === 29
231 ) && day > daysInMonth(year, month)
232 ) {
233 throw new TypeError(
234 `सूत्र: The provided day violates the day‐of‐month constraint for month ${month}: ${day}.`,
235 );
236 } else {
237 return {
238 year,
239 month,
240 day,
241 hour,
242 minute,
243 second,
244 timezoneOffset,
245 };
246 }
247 }
248 };
249
250 /**
251 * Ensures that the provided value is an integer and returns it.
252 *
253 * ☡ This function throws if the provided value is not an integer.
254 *
255 * ※ This function is not exposed.
256 */
257 const integer = ($) => {
258 if (!isIntegralNumber($)) {
259 throw new TypeError(`सूत्र: Expected an integer, but got: ${$}.`);
260 } else {
261 return $;
262 }
263 };
264
265 /**
266 * Ensures that the provided value is a nonnegative integer and returns
267 * it.
268 *
269 * ☡ This function throws if the provided value is not a nonnegative
270 * integer.
271 *
272 * ※ This function is not exposed.
273 */
274 const nonnegativeInteger = ($) => {
275 if (!(isIntegralNumber($) && $ >= 0)) {
276 throw new TypeError(
277 `सूत्र: Expected a nonnegative integer, but got: ${$}.`,
278 );
279 } else {
280 return $;
281 }
282 };
283
284 /**
285 * Ensures that the provided value is a finite number and returns it.
286 *
287 * ☡ This function throws if the provided value is not a finite number.
288 *
289 * ※ This function is not exposed.
290 */
291 const decimalNumber = ($) => {
292 if (!(isFiniteNumber($))) {
293 throw new TypeError(
294 `सूत्र: Expected a finite number, but got: ${$}.`,
295 );
296 } else {
297 return $;
298 }
299 };
300
301 /**
302 * Ensures that the provided value is a nonnegative finite number and
303 * returns it.
304 *
305 * ☡ This function throws if the provided value is not a nonnegative
306 * finite number.
307 *
308 * ※ This function is not exposed.
309 */
310 const nonnegativeDecimalNumber = ($) => {
311 if (!(isFiniteNumber($) && $ >= 0)) {
312 throw new TypeError(
313 `सूत्र: Expected a nonnegative finite number, but got: ${$}.`,
314 );
315 } else {
316 return $;
317 }
318 };
319
320 /**
321 * Ensures that the provided value is a 32‐bit float and returns it.
322 *
323 * ☡ This function throws if the provided value is not a 32‐bit float.
324 *
325 * ※ This function is not exposed.
326 */
327 const float = ($) => {
328 if (typeof $ !== "number" || !sameValue(toFloat32($), $)) {
329 throw new TypeError(
330 `सूत्र: Expected a float value, but got: ${$}.`,
331 );
332 } else {
333 return $;
334 }
335 };
336
337 /**
338 * Ensures that the provided value is a number and returns it.
339 *
340 * ☡ This function throws if the provided value is not a number.
341 *
342 * ※ This function is not exposed.
343 */
344 const double = ($) => {
345 if (typeof $ !== "number") {
346 throw new TypeError(
347 `सूत्र: Expected a double value, but got: ${$}.`,
348 );
349 } else {
350 return $;
351 }
352 };
353
354 /**
355 * Ensures that the provided value is a hexBinary value, i·e a sequence
356 * of binary octets.
357 *
358 * ☡ This function throws if the provided value is not an array buffer.
359 *
360 * ※ This function is not exposed.
361 */
362 const hexBinaryValue = ($) => {
363 if (!isArrayBuffer($)) {
364 throw new TypeError(
365 `सूत्र: Expected a hexBinary value, but got: ${$}.`,
366 );
367 } else {
368 return $;
369 }
370 };
371
372 const {
373 /**
374 * Converts a string of 0’s and 1’s into a sequence of binary digits.
375 *
376 * ※ This function is not exposed.
377 */
378 binaryDigitSequence,
379
380 /**
381 * Converts a generator iterator into a sequence of values.
382 *
383 * ※ This function is not exposed.
384 */
385 generatorSequence,
386
387 /**
388 * Converts a string into a sequence of characters.
389 *
390 * ※ This function is not exposed.
391 */
392 literalSequence,
393 } = (() => {
394 const {
395 next: generatorIteratorNext,
396 } = getPrototype(function* () {}.prototype);
397 const { [ITERATOR]: stringIterator } = String.prototype;
398 const {
399 next: stringIteratorNext,
400 } = getPrototype(""[ITERATOR]());
401 const binaryDigitIterator = function* () {
402 for (const digit of literalSequence(this)) {
403 yield digit === "0" ? 0 : 1;
404 }
405 };
406 const binaryDigitSequenceIterablePrototype = {
407 [ITERATOR]() {
408 return {
409 next: bind(
410 generatorIteratorNext,
411 call(binaryDigitIterator, this.binaryDigits, []),
412 [],
413 ),
414 };
415 },
416 };
417 const generatorSequenceIterablePrototype = {
418 [ITERATOR]() {
419 return {
420 next: bind(
421 generatorIteratorNext,
422 this.generator(),
423 [],
424 ),
425 };
426 },
427 };
428 const literalSequenceIterablePrototype = {
429 [ITERATOR]() {
430 return {
431 next: bind(
432 stringIteratorNext,
433 call(stringIterator, this.literal, []),
434 [],
435 ),
436 };
437 },
438 };
439
440 return {
441 binaryDigitSequence: (digits) =>
442 objectCreate(
443 binaryDigitSequenceIterablePrototype,
444 { binaryDigits: { value: digits } },
445 ),
446 generatorSequence: (generator) =>
447 objectCreate(
448 generatorSequenceIterablePrototype,
449 { generator: { value: generator } },
450 ),
451 literalSequence: (literal) =>
452 objectCreate(
453 literalSequenceIterablePrototype,
454 { literal: { value: literal } },
455 ),
456 };
457 })();
458
459 /**
460 * Maps each digit to its numerical value.
461 *
462 * See <https://www.w3.org/TR/xmlschema11-2/#f-digitVal>.
463 *
464 * ☡ This function throws if the provided value is not a digit string.
465 *
466 * ※ This function is not exposed.
467 */
468 const digitValue = (d) => +ensureMatches(lexicalDigit, d);
469
470 /**
471 * Maps a sequence of digits to the position‐weighted sum of the terms
472 * numerical values.
473 *
474 * See <https://www.w3.org/TR/xmlschema11-2/#f-digitSeqVal>.
475 *
476 * ☡ This function throws if the provided value is not an iterable of
477 * digit strings.
478 *
479 * ※ Although elsewhere the X·S·D specification claims that sequences
480 * count from zero, this sequence clearly must count from one.
481 *
482 * ※ This function is not exposed.
483 */
484 const digitSequenceValue = (S) => {
485 let sum = 0;
486 for (const S_i of S) {
487 sum = sum * 10 + digitValue(S_i);
488 }
489 return sum;
490 };
491
492 /**
493 * Maps a sequence of digits to the position‐weighted sum of the terms
494 * numerical values, weighted appropriately for fractional digits.
495 *
496 * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitSeqVal>.
497 *
498 * ☡ This function throws if the provided value is not an iterable of
499 * digit strings.
500 *
501 * ※ The X·S·D specification erroneously specifies ·digitValue·(Si) −
502 * 10^(−i), but ·digitValue·(Si) × 10^(−i) is correct.
503 *
504 * ※ Although elsewhere the X·S·D specification claims that sequences
505 * count from zero, this sequence clearly must count from one.
506 *
507 * ※ This function is not exposed.
508 */
509 const fractionDigitSequenceValue = (S) => {
510 let sum = 0;
511 let i = 0;
512 for (const S_i of S) {
513 sum += digitValue(S_i) * 10 ** --i;
514 }
515 return sum;
516 };
517
518 /**
519 * Maps a fracFrag to the appropriate fractional decimal number.
520 *
521 * See <https://www.w3.org/TR/xmlschema11-2/#f-fracFragVal>.
522 *
523 * ☡ This function throws if the provided value is not a fracFrag
524 * string.
525 *
526 * ※ This function is not exposed.
527 */
528 //deno-lint-ignore no-unused-vars
529 const fractionFragValue = (N) =>
530 fractionDigitSequenceValue(
531 literalSequence(ensureMatches(fracFrag, N)),
532 );
533
534 /**
535 * Maps an unsignedNoDecimalPtNumeral to its numerical value.
536 *
537 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsNoDecVal>.
538 *
539 * ☡ This function throws if the provided value is not a
540 * unsignedNoDecimalPtNumeral string.
541 */
542 export const unsignedNoDecimalMap = (N) =>
543 digitSequenceValue(
544 literalSequence(ensureMatches(unsignedNoDecimalPtNumeral, N)),
545 );
546
547 /**
548 * Maps an noDecimalPtNumeral to its numerical value.
549 *
550 * See <https://www.w3.org/TR/xmlschema11-2/#f-noDecVal>.
551 *
552 * ☡ This function throws if the provided value is not a
553 * noDecimalPtNumeral string.
554 */
555 export const noDecimalMap = (N) => {
556 switch (ensureMatches(noDecimalPtNumeral, N)[0]) {
557 case "-":
558 return -unsignedNoDecimalMap(substring(N, 1)) || 0;
559 case "+":
560 return unsignedNoDecimalMap(substring(N, 1));
561 default:
562 return unsignedNoDecimalMap(N);
563 }
564 };
565
566 /**
567 * Maps an unsignedDecimalPtNumeral to its numerical value.
568 *
569 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsDecVal>.
570 *
571 * ☡ This function throws if the provided value is not a
572 * unsignedDecimalPtNumeral string.
573 *
574 * ※ This function makes use of the built·in Ecmascript float parsing
575 * to avoid rounding errors.
576 */
577 export const unsignedDecimalPtMap = (D) => {
578 // const N = substring(
579 // ensureMatches(unsignedDecimalPtNumeral, D),
580 // 0,
581 // getFirstSubstringIndex(D, "."),
582 // );
583 // const F = substring(D, N.length + 1);
584 // if (F === "") {
585 // return unsignedNoDecimalMap(N);
586 // } else if (N === "") {
587 // return fractionFragValue(F);
588 // } else {
589 // return unsignedNoDecimalMap(N) + fractionFragValue(F);
590 // }
591 return stringToFloat(ensureMatches(unsignedDecimalPtNumeral, D));
592 };
593
594 /**
595 * Maps an decimalPtNumeral to its numerical value.
596 *
597 * See <https://www.w3.org/TR/xmlschema11-2/#f-decVal>.
598 *
599 * ☡ This function throws if the provided value is not a
600 * decimalPtNumeral string.
601 */
602 export const decimalPtMap = (N) => {
603 switch (ensureMatches(decimalPtNumeral, N)[0]) {
604 case "-":
605 return -unsignedDecimalPtMap(substring(N, 1)) || 0;
606 case "+":
607 return unsignedDecimalPtMap(substring(N, 1));
608 default:
609 return unsignedDecimalPtMap(N);
610 }
611 };
612
613 /**
614 * Maps a scientificNotationNumeral to its numerical value.
615 *
616 * See <https://www.w3.org/TR/xmlschema11-2/#f-sciVal>.
617 *
618 * ☡ This function throws if the provided value is not a
619 * scientificNotationNumeral string.
620 *
621 * ※ The X·S·D specification erroneously specifies
622 * ·unsignedDecimalPtMap·(E), but ·noDecimalMap·(E) is correct.
623 *
624 * ※ This function makes use of the built·in Ecmascript float parsing
625 * to avoid rounding errors.
626 */
627 export const scientificMap = (N) => {
628 // const C = substring(
629 // ensureMatches(scientificNotationNumeral, N),
630 // 0,
631 // (() => {
632 // let index = 0;
633 // for (const char of literalSequence(N)) {
634 // if (char === "e" || char === "E") {
635 // return index;
636 // } else {
637 // ++index;
638 // continue;
639 // }
640 // }
641 // })(),
642 // );
643 // const E = substring(N, C.length + 1);
644 // return getFirstSubstringIndex(N, ".") !== -1
645 // ? decimalPtMap(C) * 10 ** noDecimalMap(E)
646 // : noDecimalMap(C) * 10 ** noDecimalMap(E);
647 return stringToFloat(ensureMatches(scientificNotationNumeral, N)) ||
648 0;
649 };
650
651 /**
652 * Maps each integer between 0 and 9 to the corresponding digit.
653 *
654 * See <https://www.w3.org/TR/xmlschema11-2/#f-digit>.
655 *
656 * ☡ This function throws if the provided value is not an integral
657 * number between 0 and 9 inclusive.
658 *
659 * ※ This function is not exposed.
660 */
661 const digit = (i) => {
662 if (!(isIntegralNumber(i) && i >= 0 && i <= 9)) {
663 throw new TypeError(
664 `सूत्र: Expected an integral number between 0 and 9 inclusive, but got: ${i}.`,
665 );
666 } else {
667 return `${i}`;
668 }
669 };
670
671 /**
672 * Maps each nonnegative integer to a sequence of integers used by
673 * ·digitSeq· to ultimately create an unsignedNoDecimalPtNumeral.
674 *
675 * See <https://www.w3.org/TR/xmlschema11-2/#f-digitRemSeq>.
676 *
677 * ☡ This function throws if the provided value is not a nonnegative
678 * integer.
679 *
680 * ※ The sequence produced by this function is infinite.
681 *
682 * ※ This function is not exposed.
683 */
684 const digitRemainderSeq = (i) => {
685 nonnegativeInteger(i);
686 return generatorSequence(function* () {
687 let s_j = i;
688 while (true) {
689 yield s_j;
690 s_j = div(s_j, 10);
691 }
692 });
693 };
694
695 /**
696 * Maps each nonnegative integer to a sequence of integers used by
697 * ·unsignedNoDecimalPtCanonicalMap· to create an
698 * unsignedNoDecimalPtNumeral.
699 *
700 * See <https://www.w3.org/TR/xmlschema11-2/#f-digitSeq>.
701 *
702 * ☡ This function throws if the provided value is not a nonnegative
703 * integer.
704 *
705 * ※ The sequence produced by this function is infinite.
706 *
707 * ※ This function is not exposed.
708 */
709 const digitSeq = (i) => {
710 nonnegativeInteger(i);
711 return generatorSequence(function* () {
712 for (const i_j of digitRemainderSeq(i)) {
713 yield mod(i_j, 10);
714 }
715 });
716 };
717
718 /**
719 * Maps a sequence of nonnegative integers to the index before the
720 * first zero term.
721 *
722 * See <https://www.w3.org/TR/xmlschema11-2/#f-lastSigDigit>.
723 *
724 * ☡ This function throws if the values of the provided sequence are
725 * not nonnegative integers.
726 *
727 * ※ The X·S·D specification erroneously describes this function as
728 * giving the index *of* the first zero term, but it is actually the
729 * index of the last nonzero term.
730 *
731 * ※ The X·S·D specification erroneously describes this function as
732 * taking a sequence of nonnegative integers, but it is called with
733 * ·FractionDigitRemainderSeq·, which is a list of nonnegative decimal
734 * numbers.
735 *
736 * ※ This function is not exposed.
737 */
738 const lastSignificantDigit = (s) => {
739 let j = 0;
740 for (const s_j of s) {
741 if (nonnegativeDecimalNumber(s_j) === 0) {
742 return j === 0 ? 0 : j - 1;
743 } else {
744 ++j;
745 continue;
746 }
747 }
748 };
749
750 /**
751 * Maps each nonnegative decimal number less than 1 to a sequence of
752 * decimal numbers used by ·fractionDigitSeq· to ultimately create an
753 * unsignedNoDecimalPtNumeral.
754 *
755 * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitRemSeq>.
756 *
757 * ☡ This function throws if the provided value is not a nonnegative
758 * finite number less than 1.
759 *
760 * ※ The X·S·D specification erroneously specifies s_0 = f − 10 and
761 * s_(j+1) = (s_j ·mod· 1) − 10, but s_0 = f × 10 and s_(j+1) = (s_j
762 * ·mod· 1) × 10 is correct.
763 *
764 * ※ The implementation of this function uses string operations
765 * because Ecmascript does not (currently) have a lossless decimal
766 * type.
767 *
768 * ※ The sequence produced by this function is infinite.
769 *
770 * ※ This function is not exposed.
771 */
772 const FractionDigitRemainderSeq = (f) => {
773 if (!(isFiniteNumber(f) && f >= 0 && f < 1)) {
774 throw new TypeError(
775 `सूत्र: Expected a nonnegative finite number less than 1, but got: ${f}.`,
776 );
777 } else {
778 return generatorSequence(function* () {
779 let s_j = f * 10;
780 while (true) {
781 yield s_j;
782 s_j = mod(s_j, 1) * 10;
783 }
784 });
785 }
786 };
787
788 /**
789 * Maps each nonnegative decimal number less than 1 to a sequence of
790 * integers used by ·fractionDigitsCanonicalFragmentMap· to ultimately
791 * create an unsignedNoDecimalPtNumeral.
792 *
793 * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitSeq>.
794 *
795 * ☡ This function throws if the provided value is not a nonnegative
796 * finite number less than 1.
797 *
798 * ※ The sequence produced by this function is infinite.
799 *
800 * ※ This function is not exposed.
801 */
802 const fractionDigitSeq = (f) => {
803 if (!(isFiniteNumber(f) && f >= 0 && f < 1)) {
804 throw new TypeError(
805 `सूत्र: Expected a nonnegative finite number less than 1, but got: ${f}.`,
806 );
807 } else {
808 return generatorSequence(function* () {
809 for (const s_j of FractionDigitRemainderSeq(f)) {
810 yield div(s_j, 1);
811 }
812 });
813 }
814 };
815
816 /**
817 * Maps each nonnegative decimal number less than 1 to a ·literal· used
818 * by ·unsignedDecimalPtCanonicalMap· to create an
819 * unsignedDecimalPtNumeral.
820 *
821 * See <https://www.w3.org/TR/xmlschema11-2/#f-fracDigitsMap>.
822 *
823 * ☡ This function throws if the provided value is not nonnegative and
824 * less than 1.
825 *
826 * ※ This function is not exposed.
827 */
828 //deno-lint-ignore no-unused-vars
829 const fractionDigitsCanonicalFragmentMap = (f) =>
830 stringCatenate(...generatorSequence(function* () {
831 const l = lastSignificantDigit(FractionDigitRemainderSeq(f));
832 let j = 0;
833 for (const f_j of fractionDigitSeq(f)) {
834 yield digit(f_j);
835 if (j === l) {
836 break;
837 } else {
838 ++j;
839 continue;
840 }
841 }
842 }));
843
844 /**
845 * Maps a nonnegative integer to a unsignedNoDecimalPtNumeral, its
846 * ·canonical representation·.
847 *
848 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsNoDecCanFragMap>.
849 *
850 * ☡ This function throws if the provided value is not a nonnegative
851 * integer.
852 */
853 export const unsignedNoDecimalPtCanonicalMap = (i) => {
854 nonnegativeInteger(i);
855 return stringCatenate(...generatorSequence(function* () {
856 const l = lastSignificantDigit(digitRemainderSeq(i));
857 const digits = new Array(l + 1);
858 let j = 0;
859 for (const i_j of digitSeq(i)) {
860 digits[l - j] = digit(i_j);
861 if (j === l) {
862 break;
863 } else {
864 ++j;
865 continue;
866 }
867 }
868 for (j = 0; j <= l; ++j) {
869 yield digits[j];
870 }
871 }));
872 };
873
874 /**
875 * Maps an integer to a noDecimalPtNumeral, its ·canonical
876 * representation·.
877 *
878 * See <https://www.w3.org/TR/xmlschema11-2/#f-noDecCanMap>.
879 *
880 * ☡ This function throws if the provided value is not an integer.
881 */
882 export const noDecimalPtCanonicalMap = (i) =>
883 integer(i) < 0
884 ? stringCatenate("-", unsignedNoDecimalPtCanonicalMap(-i))
885 : unsignedNoDecimalPtCanonicalMap(i);
886
887 /**
888 * Maps a nonnegative decimal number to a unsignedDecimalPtNumeral, its
889 * ·canonical representation·.
890 *
891 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsDecCanFragMap>.
892 *
893 * ☡ This function throws if the provided value is not a nonnegative
894 * finite number.
895 *
896 * ※ This function makes use of the built·in Ecmascript float
897 * serialization to avoid rounding errors.
898 */
899 export const unsignedDecimalPtCanonicalMap = (n) => {
900 // nonnegativeDecimalNumber(n);
901 // return stringCatenate(
902 // unsignedNoDecimalPtCanonicalMap(div(n, 1)),
903 // ".",
904 // fractionDigitsCanonicalFragmentMap(mod(n, 1)),
905 // );
906 const exp = toExponentialNotation(nonnegativeDecimalNumber(n));
907 const eIndex = getFirstSubstringIndex(exp, "e");
908 const exponent = +substring(exp, eIndex + 1);
909 const zeroPaddedMantissa = stringPadEnd(
910 stringPadStart(
911 exp[0],
912 -exponent + 1,
913 "0",
914 ) + (eIndex > 1 ? substring(exp, 2, eIndex) : ""),
915 exponent + 2,
916 "0",
917 );
918 const decimalPoint = exponent < 0 ? 1 : exponent + 1;
919 return stringCatenate(
920 substring(zeroPaddedMantissa, 0, decimalPoint),
921 ".",
922 substring(zeroPaddedMantissa, decimalPoint),
923 );
924 };
925
926 /**
927 * Maps a decimal number to a decimalPtNumeral, its ·canonical
928 * representation·.
929 *
930 * See <https://www.w3.org/TR/xmlschema11-2/#f-decCanFragMap>.
931 *
932 * ☡ This function throws if the provided value is not a finite number.
933 */
934 export const decimalPtCanonicalMap = (n) =>
935 decimalNumber(n) < 0
936 ? stringCatenate("-", unsignedDecimalPtCanonicalMap(-n))
937 : unsignedDecimalPtCanonicalMap(n);
938
939 /**
940 * Maps a nonnegative decimal number to a
941 * unsignedScientificNotationNumeral, its ·canonical representation·.
942 *
943 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsSciCanFragMap>.
944 *
945 * ☡ This function throws if the provided value is not a nonnegative
946 * finite number.
947 *
948 * ※ This function makes use of the built·in Ecmascript float
949 * serialization to avoid rounding errors.
950 */
951 export const unsignedScientificCanonicalMap = (n) => {
952 // nonnegativeDecimalNumber(n);
953 // return stringCatenate(
954 // unsignedDecimalPtCanonicalMap(n / 10 ** div(log10(n), 1)),
955 // "E",
956 // noDecimalPtCanonicalMap(div(log10(n), 1)),
957 // );
958 const exp = toExponentialNotation(nonnegativeDecimalNumber(n));
959 const [mantissa, exponent] = stringSplit(exp, "e");
960 return stringCatenate(
961 mantissa,
962 stringIncludes(mantissa, ".") ? "E" : ".0E",
963 exponent[0] === "+" ? substring(exponent, 1) : exponent,
964 );
965 };
966
967 /**
968 * Maps a decimal number to a scientificNotationNumeral, its ·canonical
969 * representation·.
970 *
971 * See <https://www.w3.org/TR/xmlschema11-2/#f-sciCanFragMap>.
972 *
973 * ☡ This function throws if the provided value is not a finite number.
974 */
975 export const scientificCanonicalMap = (n) =>
976 decimalNumber(n) < 0
977 ? stringCatenate("-", unsignedScientificCanonicalMap(-n))
978 : unsignedScientificCanonicalMap(n);
979
980 /**
981 * Maps the ·lexical representations· of ·special values· used with
982 * some numerical datatypes to those ·special values·.
983 *
984 * See <https://www.w3.org/TR/xmlschema11-2/#f-specRepVal>.
985 *
986 * ☡ This function throws if the provided value is not a
987 * numericalSpecialRep string.
988 */
989 export const specialRepValue = (c) => {
990 switch (ensureMatches(numericalSpecialRep, c)) {
991 case "INF":
992 case "+INF":
993 return positiveInfinity;
994 case "-INF":
995 return negativeInfinity;
996 case "NaN":
997 return notANumber;
998 }
999 };
1000
1001 /**
1002 * Maps the ·special values· used with some numerical datatypes to
1003 * their ·canonical representations·.
1004 *
1005 * See <https://www.w3.org/TR/xmlschema11-2/#f-specValCanMap>.
1006 *
1007 * ☡ This function throws if the provided value is not nan or positive
1008 * or negative infinity.
1009 */
1010 export const specialRepCanonicalMap = (c) => {
1011 if (sameValue(c, positiveInfinity)) {
1012 return "INF";
1013 } else if (sameValue(c, negativeInfinity)) {
1014 return "-INF";
1015 } else if (sameValue(c, notANumber)) {
1016 return "NaN";
1017 } else {
1018 throw new TypeError(
1019 `सूत्र: Expected a special value, but got: ${c}.`,
1020 );
1021 }
1022 };
1023
1024 /**
1025 * Maps a decimalLexicalRep onto a decimal value.
1026 *
1027 * See <https://www.w3.org/TR/xmlschema11-2/#f-decimalLexmap>.
1028 *
1029 * ☡ This function throws if the provided value is not a
1030 * decimalLexicalRep string.
1031 */
1032 export const decimalLexicalMap = (LEX) => {
1033 ensureMatches(decimalLexicalRep, LEX);
1034 if (noDecimalPtNumeral(LEX)) {
1035 return noDecimalMap(LEX);
1036 } else if (decimalPtNumeral(LEX)) {
1037 return decimalPtMap(LEX);
1038 }
1039 };
1040
1041 /**
1042 * Maps a decimal to its ·canonical representation·, a
1043 * decimalLexicalRep.
1044 *
1045 * See <https://www.w3.org/TR/xmlschema11-2/#f-decimalCanmap>.
1046 *
1047 * ☡ This function throws if the provided value is not a finite number.
1048 */
1049 export const decimalCanonicalMap = (d) =>
1050 isIntegralNumber(decimalNumber(d))
1051 ? noDecimalPtCanonicalMap(d)
1052 : decimalPtCanonicalMap(d);
1053
1054 /**
1055 * Rounds a non-zero decimal number to the nearest floating-point
1056 * value.
1057 *
1058 * See <https://www.w3.org/TR/xmlschema11-2/#f-floatPtRound>.
1059 *
1060 * ☡ This function throws if the provided value is zero or is not a
1061 * finite number.
1062 *
1063 * ※ This function uses native Ecmascript float rounding methods and
1064 * only supports a cWidth of 24 or 53.
1065 *
1066 * ※ This function is not exposed.
1067 */
1068 const floatingPointRound = (nV, cWidth, eMin, eMax) => {
1069 if (!isFiniteNumber(nV) || nV == 0) {
1070 throw new TypeError(
1071 `सूत्र: Expected a finite nonzero number, but got: ${nV}.`,
1072 );
1073 } else if (cWidth !== 24 && cWidth !== 53) {
1074 throw new TypeError(
1075 `सूत्र: Unsupported cWidth: ${cWidth}.`,
1076 );
1077 } else if (
1078 cWidth === 24 && (eMin !== -149 || eMax !== 104) ||
1079 cWidth === 53 && (eMin !== -1074 || eMax !== 971)
1080 ) {
1081 throw new TypeError(
1082 `सूत्र: Unsupported eMin and eMax for cWidth: ${cWidth}.`,
1083 );
1084 } else {
1085 return cWidth <= 24 ? toFloat32(nV) : +nV;
1086 }
1087 };
1088
1089 /**
1090 * Maps a decimal number to that value rounded by some power of 10.
1091 *
1092 * See <https://www.w3.org/TR/xmlschema11-2/#f-round>.
1093 *
1094 * ☡ This function throws if the provided value is not a finite number.
1095 *
1096 * ☡ This function throws if the provided power of 10 is not a
1097 * nonnegative integer.
1098 *
1099 * ※ This function is not exposed.
1100 */
1101 const round = (n, k) => {
1102 decimalNumber(n);
1103 nonnegativeInteger(k);
1104 return div(n / 10 ** k + 0.5, 1) * 10 ** k;
1105 };
1106
1107 /**
1108 * Maps a decimal number (c × 10e) to successive approximations.
1109 *
1110 * See <https://www.w3.org/TR/xmlschema11-2/#f-round>.
1111 *
1112 * ☡ This function throws if the provided values are not a nonnegative
1113 * integer, an integer, an a nonnegative integer.
1114 *
1115 * ※ This function is not exposed.
1116 */
1117 //deno-lint-ignore no-unused-vars
1118 const floatApprox = (c, e, j) => {
1119 nonnegativeInteger(c);
1120 integer(e);
1121 nonnegativeInteger(j);
1122 return round(c, j) * 10 ** e;
1123 };
1124
1125 /**
1126 * Maps a floatRep onto a float value.
1127 *
1128 * See <https://www.w3.org/TR/xmlschema11-2/#f-floatLexmap>.
1129 *
1130 * ☡ This function throws if the provided value is not a floatRep
1131 * string.
1132 */
1133 export const floatLexicalMap = (LEX) => {
1134 ensureMatches(floatRep, LEX);
1135 if (numericalSpecialRep(LEX)) {
1136 return specialRepValue(LEX);
1137 } else {
1138 const nV = noDecimalPtNumeral(LEX)
1139 ? noDecimalMap(LEX)
1140 : decimalPtNumeral(LEX)
1141 ? decimalPtMap(LEX)
1142 : scientificMap(LEX);
1143 const v = nV == 0 ? 0 : floatingPointRound(nV, 24, -149, 104);
1144 return v == 0 ? LEX[0] === "-" ? negativeZero : positiveZero : v;
1145 }
1146 };
1147
1148 /**
1149 * Maps a doubleRep onto a double value.
1150 *
1151 * See <https://www.w3.org/TR/xmlschema11-2/#f-doubleLexmap>.
1152 *
1153 * ☡ This function throws if the provided value is not a doubleRep
1154 * string.
1155 */
1156 export const doubleLexicalMap = (LEX) => {
1157 ensureMatches(doubleRep, LEX);
1158 if (numericalSpecialRep(LEX)) {
1159 return specialRepValue(LEX);
1160 } else {
1161 const nV = noDecimalPtNumeral(LEX)
1162 ? noDecimalMap(LEX)
1163 : decimalPtNumeral(LEX)
1164 ? decimalPtMap(LEX)
1165 : scientificMap(LEX);
1166 const v = nV == 0 ? 0 : floatingPointRound(nV, 53, -1074, 971);
1167 return v == 0 ? LEX[0] === "-" ? negativeZero : positiveZero : v;
1168 }
1169 };
1170
1171 /**
1172 * Maps a float to its ·canonical representation·, a floatRep.
1173 *
1174 * See <https://www.w3.org/TR/xmlschema11-2/#f-floatCanmap>.
1175 *
1176 * ☡ This function throws if the provided value is not a 32‐bit float.
1177 */
1178 export const floatCanonicalMap = (f) => {
1179 float(f);
1180 return f === positiveInfinity || f === negativeInfinity ||
1181 sameValue(f, notANumber)
1182 ? specialRepCanonicalMap(f)
1183 : sameValue(f, 0)
1184 ? "0.0E0"
1185 : sameValue(f, -0)
1186 ? "-0.0E0"
1187 : scientificCanonicalMap(f);
1188 };
1189
1190 /**
1191 * Maps a double to its ·canonical representation·, a doubleRep.
1192 *
1193 * See <https://www.w3.org/TR/xmlschema11-2/#f-doubleCanmap>.
1194 *
1195 * ☡ This function throws if the provided value is not a number.
1196 */
1197 export const doubleCanonicalMap = (f) => {
1198 double(f);
1199 return f === positiveInfinity || f === negativeInfinity ||
1200 sameValue(f, notANumber)
1201 ? specialRepCanonicalMap(f)
1202 : sameValue(f, 0)
1203 ? "0.0E0"
1204 : sameValue(f, -0)
1205 ? "-0.0E0"
1206 : scientificCanonicalMap(f);
1207 };
1208
1209 /**
1210 * Maps a duYearFrag to an integer, intended as part of the value of
1211 * the ·months· property of a duration value.
1212 *
1213 * See <https://www.w3.org/TR/xmlschema11-2/#f-duYrMap>.
1214 *
1215 * ☡ This function throws if the provided value is not a duYearFrag
1216 * string.
1217 *
1218 * ※ The X·S·D specification erroneously specifies the numeral as
1219 * following the letter, but it precedes it.
1220 *
1221 * ※ This function is not exposed.
1222 */
1223 const duYearFragmentMap = (Y) =>
1224 noDecimalMap(stringSlice(ensureMatches(duYearFrag, Y), 0, -1));
1225
1226 /**
1227 * Maps a duMonthFrag to an integer, intended as part of the value of
1228 * the ·months· property of a duration value.
1229 *
1230 * See <https://www.w3.org/TR/xmlschema11-2/#f-duMoMap>.
1231 *
1232 * ☡ This function throws if the provided value is not a duMonthFrag
1233 * string.
1234 *
1235 * ※ The X·S·D specification erroneously specifies the numeral as
1236 * following the letter, but it precedes it.
1237 *
1238 * ※ This function is not exposed.
1239 */
1240 const duMonthFragmentMap = (M) =>
1241 noDecimalMap(stringSlice(ensureMatches(duMonthFrag, M), 0, -1));
1242
1243 /**
1244 * Maps a duDayFrag to an integer, intended as part of the value of the
1245 * ·seconds· property of a duration value.
1246 *
1247 * See <https://www.w3.org/TR/xmlschema11-2/#f-duDaMap>.
1248 *
1249 * ☡ This function throws if the provided value is not a duDayFrag
1250 * string.
1251 *
1252 * ※ The X·S·D specification erroneously specifies the numeral as
1253 * following the letter, but it precedes it.
1254 *
1255 * ※ This function is not exposed.
1256 */
1257 const duDayFragmentMap = (D) =>
1258 noDecimalMap(stringSlice(ensureMatches(duDayFrag, D), 0, -1));
1259
1260 /**
1261 * Maps a duHourFrag to an integer, intended as part of the value of
1262 * the ·seconds· property of a duration value.
1263 *
1264 * See <https://www.w3.org/TR/xmlschema11-2/#f-duHrMap>.
1265 *
1266 * ☡ This function throws if the provided value is not a duHourFrag
1267 * string.
1268 *
1269 * ※ The X·S·D specification erroneously specifies the numeral as
1270 * following the letter, but it precedes it.
1271 *
1272 * ※ The X·S·D specification has a copypasta typo in the definition of
1273 * this function; it takes an argument H which necessarily is followed
1274 * by an "H". There is no D which necessarily begins with "D".
1275 *
1276 * ※ This function is not exposed.
1277 */
1278 const duHourFragmentMap = (H) =>
1279 noDecimalMap(stringSlice(ensureMatches(duHourFrag, H), 0, -1));
1280
1281 /**
1282 * Maps a duMinuteFrag to an integer, intended as part of the value of
1283 * the ·seconds· property of a duration value.
1284 *
1285 * See <https://www.w3.org/TR/xmlschema11-2/#f-duMiMap>.
1286 *
1287 * ☡ This function throws if the provided value is not a duMinuteFrag
1288 * string.
1289 *
1290 * ※ The X·S·D specification erroneously specifies the numeral as
1291 * following the letter, but it precedes it.
1292 *
1293 * ※ This function is not exposed.
1294 */
1295 const duMinuteFragmentMap = (M) =>
1296 noDecimalMap(stringSlice(ensureMatches(duMinuteFrag, M), 0, -1));
1297
1298 /**
1299 * Maps a duSecondFrag to an integer, intended as part of the value of
1300 * the ·seconds· property of a duration value.
1301 *
1302 * See <https://www.w3.org/TR/xmlschema11-2/#f-duSeMap>.
1303 *
1304 * ☡ This function throws if the provided value is not a duMinuteFrag
1305 * string.
1306 *
1307 * ※ The X·S·D specification erroneously specifies the numeral as
1308 * following the letter, but it precedes it.
1309 *
1310 * ※ This function is not exposed.
1311 */
1312 const duSecondFragmentMap = (S) =>
1313 getFirstSubstringIndex(ensureMatches(duSecondFrag, S), ".") !== -1
1314 ? decimalPtMap(stringSlice(S, 0, -1))
1315 : noDecimalMap(stringSlice(S, 0, -1));
1316
1317 /**
1318 * Maps a duYearMonthFrag into an integer, intended as part of the
1319 * ·months· property of a duration value.
1320 *
1321 * See <https://www.w3.org/TR/xmlschema11-2/#f-duYMMap>.
1322 *
1323 * ☡ This function throws if the provided value is not a
1324 * duYearMonthFrag string.
1325 *
1326 * ※ This function is not exposed.
1327 */
1328 const duYearMonthFragmentMap = (YM) => {
1329 let Y, M;
1330 new Matcher(
1331 rawString`(?<Y>\d+Y)?(?<M>\d+M)?`,
1332 undefined,
1333 (_, { groups }) => {
1334 Y = groups.Y;
1335 M = groups.M;
1336 return true;
1337 },
1338 )(ensureMatches(duYearMonthFrag, YM));
1339 const y = Y ? duYearFragmentMap(Y) : 0;
1340 const m = M ? duMonthFragmentMap(M) : 0;
1341 return 12 * y + m;
1342 };
1343
1344 /**
1345 * Maps a duTimeFrag into an integer, intended as part of the ·seconds·
1346 * property of a duration value.
1347 *
1348 * See <https://www.w3.org/TR/xmlschema11-2/#f-duTMap>.
1349 *
1350 * ☡ This function throws if the provided value is not a duTimeFrag
1351 * string.
1352 *
1353 * ※ The X·S·D specification erroneously applies ·duDayFragmentMap· to
1354 * H; ·duHourFragmentMap· is correct.
1355 *
1356 * ※ This function is not exposed.
1357 */
1358 const duTimeFragmentMap = (T) => {
1359 let H, M, S;
1360 new Matcher(
1361 rawString`T(?<H>\d+H)?(?<M>\d+M)?(?<S>[\d.]+S)?`,
1362 undefined,
1363 (_, { groups }) => {
1364 H = groups.H;
1365 M = groups.M;
1366 S = groups.S;
1367 return true;
1368 },
1369 )(ensureMatches(duTimeFrag, T));
1370 const h = H ? duHourFragmentMap(H) : 0;
1371 const m = M ? duMinuteFragmentMap(M) : 0;
1372 const s = S ? duSecondFragmentMap(S) : 0;
1373 return 3600 * h + 60 * m + s;
1374 };
1375
1376 /**
1377 * Maps a duDayTimeFrag into a decimal number, which is the potential
1378 * value of the ·seconds· property of a duration value.
1379 *
1380 * See <https://www.w3.org/TR/xmlschema11-2/#f-duDTMap>.
1381 *
1382 * ☡ This function throws if the provided value is not a duDayTimeFrag
1383 * string.
1384 *
1385 * ※ This function is not exposed.
1386 */
1387 const duDayTimeFragmentMap = (DT) => {
1388 let D, T;
1389 new Matcher(
1390 rawString`(?<D>\d+D)?(?<T>T.+)?`,
1391 undefined,
1392 (_, { groups }) => {
1393 D = groups.D;
1394 T = groups.T;
1395 return true;
1396 },
1397 )(ensureMatches(duDayTimeFrag, DT));
1398 const d = D ? duDayFragmentMap(D) : 0;
1399 const t = T ? duTimeFragmentMap(T) : 0;
1400 return 86400 * d + t;
1401 };
1402
1403 /**
1404 * Separates the durationLexicalRep into the month part and the seconds
1405 * part, then maps them into the ·months· and ·seconds· of the duration
1406 * value.
1407 *
1408 * See <https://www.w3.org/TR/xmlschema11-2/#f-durationMap>.
1409 *
1410 * ☡ This function throws if the provided value is not a
1411 * durationLexicalRep string.
1412 */
1413 export const durationMap = (DUR) => {
1414 let Y, D;
1415 new Matcher(
1416 rawString`-?P(?<Y>(?:\d+Y)?(?:\d+M)?)(?<D>(?:\d+D)?(?:T.+)?)`,
1417 undefined,
1418 (_, { groups }) => {
1419 Y = groups.Y;
1420 D = groups.D;
1421 return true;
1422 },
1423 )(ensureMatches(durationLexicalRep, DUR));
1424 const s = DUR[0] === "-" ? -1 : 1;
1425 return {
1426 months: Y ? s * duYearMonthFragmentMap(Y) + 0 : 0, // cast −0 to 0
1427 seconds: D ? s * duDayTimeFragmentMap(D) + 0 : 0, // cast −0 to 0
1428 };
1429 };
1430
1431 /**
1432 * Maps the lexical representation into the ·months· of a
1433 * yearMonthDuration value. (A yearMonthDuration's ·seconds· is always
1434 * zero.) ·yearMonthDurationMap· is a restriction of ·durationMap·.
1435 *
1436 * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationMap>.
1437 *
1438 * ☡ This function throws if the provided value is not a
1439 * yearMonthDurationLexicalRep string.
1440 */
1441 export const yearMonthDurationMap = (YM) => {
1442 const s = YM[0] === "-" ? -1 : 1;
1443 const Y = substring(
1444 ensureMatches(yearMonthDurationLexicalRep, YM),
1445 s === -1 ? 2 : 1,
1446 );
1447 return {
1448 months: s * duYearMonthFragmentMap(Y) + 0, // cast −0 to 0
1449 seconds: 0,
1450 };
1451 };
1452
1453 /**
1454 * Maps the lexical representation into the ·seconds· of a
1455 * dayTimeDuration value. (A dayTimeDuration's ·months· is always
1456 * zero.) ·dayTimeDurationMap· is a restriction of ·durationMap·.
1457 *
1458 * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationMap>.
1459 *
1460 * ☡ This function throws if the provided value is not a
1461 * dayTimeDurationLexicalRep string.
1462 *
1463 * ※ The X·S·D specification erroneously describes the argument as a
1464 * dayTimeDuration value rather than a dayTimeDurationLexicalRep.
1465 */
1466 export const dayTimeDurationMap = (DT) => {
1467 const s = DT[0] === "-" ? -1 : 1;
1468 const D = substring(
1469 ensureMatches(dayTimeDurationLexicalRep, DT),
1470 s === -1 ? 2 : 1,
1471 );
1472 return {
1473 months: 0,
1474 seconds: s * duDayTimeFragmentMap(D) + 0, // cast −0 to 0
1475 };
1476 };
1477
1478 /**
1479 * Maps a nonnegative integer, presumably the absolute value of the
1480 * ·months· of a duration value, to a duYearMonthFrag, a fragment of a
1481 * duration ·lexical representation·.
1482 *
1483 * See <https://www.w3.org/TR/xmlschema11-2/#f-duYMCan>.
1484 *
1485 * ☡ This function throws if the provided value is not a nonnegative
1486 * integer.
1487 *
1488 * ※ This function is not exposed.
1489 */
1490 const duYearMonthCanonicalFragmentMap = (ym) => {
1491 nonnegativeInteger(ym);
1492 const y = div(ym, 12);
1493 const m = mod(ym, 12);
1494 return y !== 0 && m !== 0
1495 ? stringCatenate(
1496 unsignedNoDecimalPtCanonicalMap(y),
1497 "Y",
1498 unsignedNoDecimalPtCanonicalMap(m),
1499 "M",
1500 )
1501 : y !== 0
1502 ? stringCatenate(
1503 unsignedNoDecimalPtCanonicalMap(y),
1504 "Y",
1505 )
1506 : stringCatenate(
1507 unsignedNoDecimalPtCanonicalMap(m),
1508 "M",
1509 );
1510 };
1511
1512 /**
1513 * Maps a nonnegative integer, presumably the day normalized value from
1514 * the ·seconds· of a duration value, to a duDayFrag, a fragment of a
1515 * duration ·lexical representation·.
1516 *
1517 * See <https://www.w3.org/TR/xmlschema11-2/#f-duDCan>.
1518 *
1519 * ☡ This function throws if the provided value is not a nonnegative
1520 * integer.
1521 *
1522 * ※ Despite the description, this function does not necessarily
1523 * return a duDayFrag (it can return an empty string).
1524 *
1525 * ※ This function is not exposed.
1526 */
1527 const duDayCanonicalFragmentMap = (d) =>
1528 d !== 0
1529 ? stringCatenate(
1530 unsignedNoDecimalPtCanonicalMap(d),
1531 "D",
1532 )
1533 : "";
1534
1535 /**
1536 * Maps a nonnegative integer, presumably the hour normalized value
1537 * from the ·seconds· of a duration value, to a duHourFrag, a fragment
1538 * of a duration ·lexical representation·.
1539 *
1540 * See <https://www.w3.org/TR/xmlschema11-2/#f-duHCan>.
1541 *
1542 * ☡ This function throws if the provided value is not a nonnegative
1543 * integer.
1544 *
1545 * ※ Despite the description, this function does not necessarily
1546 * return a duHourFrag (it can return an empty string).
1547 *
1548 * ※ This function is not exposed.
1549 */
1550 const duHourCanonicalFragmentMap = (h) =>
1551 h !== 0
1552 ? stringCatenate(
1553 unsignedNoDecimalPtCanonicalMap(h),
1554 "H",
1555 )
1556 : "";
1557
1558 /**
1559 * Maps a nonnegative integer, presumably the minute normalized value
1560 * from the ·seconds· of a duration value, to a duMinuteFrag, a
1561 * fragment of a duration ·lexical representation·.
1562 *
1563 * See <https://www.w3.org/TR/xmlschema11-2/#f-duMCan>.
1564 *
1565 * ☡ This function throws if the provided value is not a nonnegative
1566 * integer.
1567 *
1568 * ※ Despite the description, this function does not necessarily
1569 * return a duMinuteFrag (it can return an empty string).
1570 *
1571 * ※ This function is not exposed.
1572 */
1573 const duMinuteCanonicalFragmentMap = (m) =>
1574 m !== 0
1575 ? stringCatenate(
1576 unsignedNoDecimalPtCanonicalMap(m),
1577 "M",
1578 )
1579 : "";
1580
1581 /**
1582 * Maps a nonnegative decimal number, presumably the second normalized
1583 * value from the ·seconds· of a duration value, to a duSecondFrag, a
1584 * fragment of a duration ·lexical representation·.
1585 *
1586 * See <https://www.w3.org/TR/xmlschema11-2/#f-duSCan>.
1587 *
1588 * ☡ This function throws if the provided value is not a nonnegative
1589 * finite number.
1590 *
1591 * ※ Despite the description, this function does not necessarily
1592 * return a duSecondFrag (it can return an empty string).
1593 *
1594 * ※ This function is not exposed.
1595 */
1596 const duSecondCanonicalFragmentMap = (s) =>
1597 s !== 0
1598 ? stringCatenate(
1599 isIntegralNumber(s)
1600 ? unsignedNoDecimalPtCanonicalMap(s)
1601 : unsignedDecimalPtCanonicalMap(s),
1602 "S",
1603 )
1604 : "";
1605
1606 /**
1607 * Maps three nonnegative numbers, presumably the hour, minute, and
1608 * second normalized values from a duration's ·seconds·, to a
1609 * duTimeFrag, a fragment of a duration ·lexical representation·.
1610 *
1611 * See <https://www.w3.org/TR/xmlschema11-2/#f-duTCan>.
1612 *
1613 * ☡ This function throws if either of the first two provided values
1614 * are not nonnegative integers, or if the final provided value is not
1615 * a nonnegative finite number.
1616 *
1617 * ※ Despite the description, this function does not necessarily
1618 * return a duTimeFrag (it can return an empty string).
1619 *
1620 * ※ This function is not exposed.
1621 */
1622 const duTimeCanonicalFragmentMap = (h, m, s) =>
1623 h !== 0 || m !== 0 || s !== 0
1624 ? stringCatenate(
1625 "T",
1626 duHourCanonicalFragmentMap(h),
1627 duMinuteCanonicalFragmentMap(m),
1628 duSecondCanonicalFragmentMap(s),
1629 )
1630 : "";
1631
1632 /**
1633 * Maps a nonnegative decimal number, presumably the absolute value of
1634 * the ·seconds· of a duration value, to a duDayTimeFrag, a fragment of
1635 * a duration ·lexical representation·.
1636 *
1637 * See <https://www.w3.org/TR/xmlschema11-2/#f-duDTCan>.
1638 *
1639 * ☡ This function throws if the provided value is not a nonnegative
1640 * finite number.
1641 *
1642 * ※ This function is not exposed.
1643 */
1644 const duDayTimeCanonicalFragmentMap = (ss) => {
1645 nonnegativeDecimalNumber(ss);
1646 const d = div(ss, 86400);
1647 const h = div(mod(ss, 86400), 3600);
1648 const m = div(mod(ss, 3600), 60);
1649 const s = mod(ss, 60);
1650 return ss !== 0
1651 ? stringCatenate(
1652 duDayCanonicalFragmentMap(d),
1653 duTimeCanonicalFragmentMap(h, m, s),
1654 )
1655 : "T0S";
1656 };
1657
1658 /**
1659 * Maps a duration's property values to durationLexicalRep fragments
1660 * and combines the fragments into a complete durationLexicalRep.
1661 *
1662 * See <https://www.w3.org/TR/xmlschema11-2/#f-durationCanMap>.
1663 *
1664 * ☡ This function throws if the provided value is not a complete
1665 * duration value.
1666 */
1667 export const durationCanonicalMap = (v) => {
1668 const { months: m, seconds: s } = duration(v);
1669 const sgn = m < 0 || s < 0 ? "-" : "";
1670 return m !== 0 && s !== 0
1671 ? stringCatenate(
1672 sgn,
1673 "P",
1674 duYearMonthCanonicalFragmentMap(abs(m)),
1675 duDayTimeCanonicalFragmentMap(abs(s)),
1676 )
1677 : m !== 0
1678 ? stringCatenate(
1679 sgn,
1680 "P",
1681 duYearMonthCanonicalFragmentMap(abs(m)),
1682 )
1683 : stringCatenate(
1684 sgn,
1685 "P",
1686 duDayTimeCanonicalFragmentMap(abs(s)),
1687 );
1688 };
1689
1690 /**
1691 * Maps a yearMonthDuration's ·months· value to a
1692 * yearMonthDurationLexicalRep. (The ·seconds· value is necessarily
1693 * zero and is ignored.) ·yearMonthDurationCanonicalMap· is a
1694 * restriction of ·durationCanonicalMap·.
1695 *
1696 * See <https://www.w3.org/TR/xmlschema11-2/#f-yearMonthDurationCanMap>.
1697 *
1698 * ☡ This function throws if the provided value is not a complete
1699 * yearMonthDuration value.
1700 */
1701 export const yearMonthDurationCanonicalMap = (ym) => {
1702 const { months: m, seconds: s } = duration(ym);
1703 if (s !== 0) {
1704 throw new TypeError(
1705 `सूत्र: Expected the provided yearMonthDuration to have a value of zero for seconds, but got: ${s}.`,
1706 );
1707 } else {
1708 const sgn = m < 0 ? "-" : "";
1709 return stringCatenate(
1710 sgn,
1711 "P",
1712 duYearMonthCanonicalFragmentMap(abs(m)),
1713 );
1714 }
1715 };
1716
1717 /**
1718 * Maps a dayTimeDuration's ·seconds· value to a
1719 * dayTimeDurationLexicalRep. (The ·months· value is necessarily zero
1720 * and is ignored.) ·dayTimeDurationCanonicalMap· is a restriction of
1721 * ·durationCanonicalMap·.
1722 *
1723 * See <https://www.w3.org/TR/xmlschema11-2/#f-dayTimeDurationCanMap>.
1724 *
1725 * ☡ This function throws if the provided value is not a complete
1726 * dayTimeDuration value.
1727 */
1728 export const dayTimeDurationCanonicalMap = (dt) => {
1729 const { months: m, seconds: s } = duration(dt);
1730 if (m !== 0) {
1731 throw new TypeError(
1732 `सूत्र: Expected the provided dayTimeDuration to have a value of zero for months, but got: ${m}.`,
1733 );
1734 } else {
1735 const sgn = s < 0 ? "-" : "";
1736 return stringCatenate(
1737 sgn,
1738 "P",
1739 duDayTimeCanonicalFragmentMap(abs(s)),
1740 );
1741 }
1742 };
1743
1744 /**
1745 * If month (mo) is out of range, adjust month and year (yr)
1746 * accordingly; otherwise, make no change.
1747 *
1748 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normMo>.
1749 *
1750 * ☡ This function throws if the provided values are not integers.
1751 *
1752 * ※ This function is not exposed.
1753 */
1754 const normalizeMonth = (yr, mo) => {
1755 integer(yr);
1756 integer(mo);
1757 return [yr + div(mo - 1, 12), mod(mo - 1, 12) + 1];
1758 };
1759
1760 /**
1761 * If month (mo) is out of range, or day (da) is out of range for the
1762 * appropriate month, then adjust values accordingly, otherwise make no
1763 * change.
1764 *
1765 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normDa>.
1766 *
1767 * ☡ This function throws if the provided values are not integers.
1768 *
1769 * ※ This function is not exposed.
1770 */
1771 const normalizeDay = (yr, mo, da) => {
1772 integer(yr);
1773 integer(mo);
1774 integer(da);
1775 [yr, mo] = normalizeMonth(yr, mo);
1776 let limit = daysInMonth(yr, mo);
1777 while (da > limit || da <= 0) {
1778 if (da > limit) {
1779 da -= limit;
1780 mo += 1;
1781 [yr, mo] = normalizeMonth(yr, mo);
1782 limit = daysInMonth(yr, mo);
1783 }
1784 if (da <= 0) {
1785 mo -= 1;
1786 [yr, mo] = normalizeMonth(yr, mo);
1787 limit = daysInMonth(yr, mo);
1788 da += limit;
1789 }
1790 }
1791 return [yr, mo, da];
1792 };
1793
1794 /**
1795 * Normalizes minute, hour, month, and year values to values that obey
1796 * the appropriate constraints.
1797 *
1798 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normMi>.
1799 *
1800 * ☡ This function throws if the provided values are not integers.
1801 *
1802 * ※ This function is not exposed.
1803 */
1804 const normalizeMinute = (yr, mo, da, hr, mi) => {
1805 integer(yr);
1806 integer(mo);
1807 integer(da);
1808 integer(hr);
1809 integer(mi);
1810 hr += div(mi, 60);
1811 mi = mod(mi, 60);
1812 da += div(hr, 24);
1813 hr = mod(hr, 24);
1814 [yr, mo, da] = normalizeDay(yr, mo, da);
1815 return [yr, mo, da, hr, mi];
1816 };
1817
1818 /**
1819 * Normalizes minute, hour, month, and year values to values that obey
1820 * the appropriate constraints. (This algorithm ignores leap seconds.)
1821 *
1822 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-normSe>.
1823 *
1824 * ☡ This function throws if the first five provided values are not
1825 * integers or the sixth is not a finite number.
1826 *
1827 * ※ This function is not exposed.
1828 */
1829 const normalizeSecond = (yr, mo, da, hr, mi, se) => {
1830 integer(yr);
1831 integer(mo);
1832 integer(da);
1833 integer(hr);
1834 integer(mi);
1835 decimalNumber(se);
1836 mi += div(se, 60);
1837 se = mod(se, 60);
1838 [yr, mo, da, hr, mi] = normalizeMinute(yr, mo, da, hr, mi);
1839 return [yr, mo, da, hr, mi, se];
1840 };
1841
1842 /**
1843 * Normalizes minute, hour, month, and year values to values that obey
1844 * the appropriate constraints. (This algorithm ignores leap seconds.)
1845 *
1846 * See <https://www.w3.org/TR/xmlschema11-2/#f-daysInMonth>.
1847 *
1848 * ☡ This function throws if the first value is not absent or an
1849 * integer or if the second value is not an integer between 1 and 12
1850 * inclusive.
1851 */
1852 export const daysInMonth = (y, m) => {
1853 if (y !== absent && !isIntegralNumber(y)) {
1854 throw new TypeError(
1855 `सूत्र: Expected an optional integer, but got: ${y}.`,
1856 );
1857 } else if (!(isIntegralNumber(m) && m >= 1 && m <= 12)) {
1858 throw new TypeError(
1859 `सूत्र: Expected an integer between 1 and 12 inclusive, but got: ${m}.`,
1860 );
1861 } else {
1862 return m === 2
1863 ? y === absent || y % 4 || !(y % 100) && y % 400 ? 28 : 29
1864 : m === 4 || m === 6 || m === 9 || m === 11
1865 ? 30
1866 : 31;
1867 }
1868 };
1869
1870 /**
1871 * Returns an instance of the date/timeSevenPropertyModel with property
1872 * values as specified in the arguments. If an argument is omitted, the
1873 * corresponding property is set to absent.
1874 *
1875 * See <https://www.w3.org/TR/xmlschema11-2/#p-setDTFromRaw>.
1876 *
1877 * ※ The X·S·D specification erroneously fails to account for the
1878 * value `"--02-29"`, which `·newDateTime·` treats as 01 March because
1879 * it assumes February has 28 days when the year is absent. This is a
1880 * correct assumption generally, but February should be treated as
1881 * having 29 days when a value of `"--02-29"` is explicity given.
1882 *
1883 * ☡ This function throws if the provided values do not match the
1884 * ranges expected by the Date∕time Seven‐Property Model.
1885 */
1886 export const newDateTime = (Yr, Mo, Da, Hr, Mi, Se, Tz) => {
1887 if (Yr !== absent && !isIntegralNumber(Yr)) {
1888 throw new TypeError(
1889 `सूत्र: Expected an optional integer, but got: ${Yr}.`,
1890 );
1891 } else if (
1892 Mo !== absent && !(isIntegralNumber(Mo) && Mo >= 1 && Mo <= 12)
1893 ) {
1894 throw new TypeError(
1895 `सूत्र: Expected an optional integer between 1 and 12 inclusive, but got: ${Mo}.`,
1896 );
1897 } else if (
1898 Da !== absent && !(isIntegralNumber(Da) && Da >= 1 && Da <= 31)
1899 ) {
1900 throw new TypeError(
1901 `सूत्र: Expected an optional integer between 1 and 31 inclusive, but got: ${Da}.`,
1902 );
1903 } else if (
1904 Hr !== absent && !(isIntegralNumber(Hr) && Hr >= 0 && Hr <= 24)
1905 ) {
1906 throw new TypeError(
1907 `सूत्र: Expected an optional integer between 0 and 24 inclusive, but got: ${Hr}.`,
1908 );
1909 } else if (
1910 Mi !== absent && !(isIntegralNumber(Mi) && Mi >= 0 && Mi <= 59)
1911 ) {
1912 throw new TypeError(
1913 `सूत्र: Expected an optional integer between 0 and 59 inclusive, but got: ${Mi}.`,
1914 );
1915 } else if (
1916 Se !== absent && !(isFiniteNumber(Se) && Se >= 0 && Se < 60)
1917 ) {
1918 throw new TypeError(
1919 `सूत्र: Expected an optional finite number greater than or equal to 0 and less than 60, but got: ${Se}.`,
1920 );
1921 } else if (
1922 Tz !== absent && !(isIntegralNumber(Tz) && Tz >= -840 && Tz <= 840)
1923 ) {
1924 throw new TypeError(
1925 `सूत्र: Expected an optional integer between -840 and 840 inclusive, but got: ${Tz}.`,
1926 );
1927 } else {
1928 let yr = Yr !== absent ? Yr : Mo === 2 && Da === 29 ? 0 : 1;
1929 let mo = Mo !== absent ? Mo : 1;
1930 let da = Da !== absent ? Da : 1;
1931 let hr = Hr !== absent ? Hr : 0;
1932 let mi = Mi !== absent ? Mi : 0;
1933 let se = Se !== absent ? Se : 0;
1934 [yr, mo, da, hr, mi, se] = normalizeSecond(yr, mo, da, hr, mi, se);
1935 return {
1936 year: Yr === absent ? absent : yr,
1937 month: Mo === absent ? absent : mo,
1938 day: Da === absent ? absent : da,
1939 hour: Hr === absent ? absent : hr,
1940 minute: Mi === absent ? absent : mi,
1941 second: Se === absent ? absent : se,
1942 timezoneOffset: Tz,
1943 };
1944 }
1945 };
1946
1947 /**
1948 * Adds a duration to a dateTime value, producing another dateTime
1949 * value.
1950 *
1951 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dt-dateTimePlusDuration>.
1952 *
1953 * ※ The X·S·D specification erroneously fails to account for the
1954 * value `"--02-29"`, which `·dateTimePlusDuration·` treats as 01 March
1955 * because it assumes February has 28 days when the year is absent.
1956 * This is a correct assumption generally, but February should be
1957 * treated as having 29 days when a value of `"--02-29"` is explicity
1958 * given.
1959 *
1960 * ☡ This function throws if the provided duration or
1961 * date/timeSevenPropertyModel values are not valid.
1962 *
1963 * ※ Despite the name and description, this function can add a
1964 * duration to any date/timeSevenPropertyModel value.
1965 */
1966 export const dateTimePlusDuration = (du, dt) => {
1967 const { months, seconds } = duration(du);
1968 const {
1969 year,
1970 month,
1971 day,
1972 hour,
1973 minute,
1974 second,
1975 timezoneOffset,
1976 } = date·timeSevenPropertyModel(dt);
1977 let yr = year !== absent ? year : month === 2 && day === 29 ? 0 : 1;
1978 let mo = month !== absent ? month : 1;
1979 let da = day !== absent ? day : 1;
1980 let hr = hour !== absent ? hour : 0;
1981 let mi = minute !== absent ? minute : 0;
1982 let se = second !== absent ? second : 0;
1983 mo += months;
1984 [yr, mo] = normalizeMonth(yr, mo);
1985 da = min(da, daysInMonth(yr, mo));
1986 se += seconds;
1987 [yr, mo, da, hr, mi, se] = normalizeSecond(yr, mo, da, hr, mi, se);
1988 return newDateTime(
1989 year === absent ? absent : yr,
1990 month === absent ? absent : mo,
1991 day === absent ? absent : da,
1992 hour === absent ? absent : hr,
1993 minute === absent ? absent : mi,
1994 second === absent ? absent : se,
1995 timezoneOffset,
1996 );
1997 };
1998
1999 /**
2000 * Maps a date/timeSevenPropertyModel value to the decimal number
2001 * representing its position on the "time line".
2002 *
2003 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dt-timeOnTimeline>.
2004 *
2005 * ☡ This function throws if the provided date/timeSevenPropertyModel
2006 * value is not valid.
2007 */
2008 export const timeOnTimeline = (dt) => {
2009 const {
2010 year,
2011 month,
2012 day,
2013 hour,
2014 minute,
2015 second,
2016 timezoneOffset,
2017 } = date·timeSevenPropertyModel(dt);
2018 const yr = year === absent ? 1971 : year - 1;
2019 const mo = month === absent ? 12 : month;
2020 const da = day === absent ? daysInMonth(yr + 1, mo) - 1 : day - 1;
2021 const hr = hour === absent ? 0 : hour;
2022 const mi = (minute === absent ? 0 : minute) -
2023 (timezoneOffset === absent ? 0 : timezoneOffset);
2024 const se = second === absent ? 0 : second;
2025 let ToTI = 31536000 * yr;
2026 ToTI += 86400 * (div(yr, 400) - div(yr, 100) + div(yr, 4));
2027 for (let m = 1; m < mo; ++m) {
2028 ToTI += 86400 * daysInMonth(yr + 1, m);
2029 }
2030 ToTI += 86400 * da;
2031 ToTI += 3600 * hr + 60 * mi + se;
2032 return ToTI;
2033 };
2034
2035 /**
2036 * Maps a yearFrag, part of a date/timeSevenPropertyModel's ·lexical
2037 * representation·, onto an integer, presumably the ·year· property of
2038 * a date/timeSevenPropertyModel value.
2039 *
2040 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-yrMap>.
2041 *
2042 * ☡ This function throws if the provided value is not a yearFrag
2043 * string.
2044 */
2045 export const yearFragValue = (YR) =>
2046 noDecimalMap(ensureMatches(yearFrag, YR));
2047
2048 /**
2049 * Maps a monthFrag, part of a date/timeSevenPropertyModel's ·lexical
2050 * representation·, onto an integer, presumably the ·month· property of
2051 * a date/timeSevenPropertyModel value.
2052 *
2053 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-moMap>.
2054 *
2055 * ☡ This function throws if the provided value is not a monthFrag
2056 * string.
2057 */
2058 export const monthFragValue = (MO) =>
2059 unsignedNoDecimalMap(ensureMatches(monthFrag, MO));
2060
2061 /**
2062 * Maps a dayFrag, part of a date/timeSevenPropertyModel's ·lexical
2063 * representation·, onto an integer, presumably the ·day· property of a
2064 * date/timeSevenPropertyModel value.
2065 *
2066 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-daMap>.
2067 *
2068 * ☡ This function throws if the provided value is not a dayFrag
2069 * string.
2070 */
2071 export const dayFragValue = (DA) =>
2072 unsignedNoDecimalMap(ensureMatches(dayFrag, DA));
2073
2074 /**
2075 * Maps a hourFrag, part of a date/timeSevenPropertyModel's ·lexical
2076 * representation·, onto an integer, presumably the ·hour· property of
2077 * a date/timeSevenPropertyModel value.
2078 *
2079 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-hrMap>.
2080 *
2081 * ☡ This function throws if the provided value is not a hourFrag
2082 * string.
2083 */
2084 export const hourFragValue = (HR) =>
2085 unsignedNoDecimalMap(ensureMatches(hourFrag, HR));
2086
2087 /**
2088 * Maps a minuteFrag, part of a date/timeSevenPropertyModel's ·lexical
2089 * representation·, onto an integer, presumably the ·minute· property
2090 * of a date/timeSevenPropertyModel value.
2091 *
2092 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-miMap>.
2093 *
2094 * ☡ This function throws if the provided value is not a minuteFrag
2095 * string.
2096 */
2097 export const minuteFragValue = (MI) =>
2098 unsignedNoDecimalMap(ensureMatches(minuteFrag, MI));
2099
2100 /**
2101 * Maps a secondFrag, part of a date/timeSevenPropertyModel's ·lexical
2102 * representation·, onto a decimal number, presumably the ·second·
2103 * property of a date/timeSevenPropertyModel value.
2104 *
2105 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-seMap>.
2106 *
2107 * ☡ This function throws if the provided value is not a secondFrag
2108 * string.
2109 */
2110 export const secondFragValue = (SE) =>
2111 getFirstSubstringIndex(ensureMatches(secondFrag, SE), ".") === -1
2112 ? unsignedNoDecimalMap(SE)
2113 : unsignedDecimalPtMap(SE);
2114
2115 /**
2116 * Maps a timezoneFrag, part of a date/timeSevenPropertyModel's
2117 * ·lexical representation·, onto an integer, presumably the
2118 * ·timezoneOffset· property of a date/timeSevenPropertyModel value.
2119 *
2120 * See <https://www.w3.org/TR/xmlschema11-2/#f-dt-tzMap>.
2121 *
2122 * ※ The X·S·D specification erroneously specifies
2123 * ·unsignedDecimalPtMap·(H) and ·unsignedDecimalPtMap·(M), but
2124 * ·unsignedNoDecimalMap·(H) and ·unsignedDecimalPtMap·(M) is correct.
2125 *
2126 * ☡ This function throws if the provided value is not a timezoneFrag
2127 * string.
2128 */
2129 export const timezoneFragValue = (TZ) => {
2130 const s = ensureMatches(timezoneFrag, TZ)[0];
2131 if (s === "Z") {
2132 return 0;
2133 } else {
2134 let H, M;
2135 new Matcher(
2136 rawString`[+-](?<H>\d{2}):(?<M>\d{2})`,
2137 undefined,
2138 (_, { groups }) => {
2139 H = groups.H;
2140 M = groups.M;
2141 return true;
2142 },
2143 )(TZ);
2144 return s === "-"
2145 ? -(unsignedNoDecimalMap(H) * 60 + unsignedNoDecimalMap(M)) + 0
2146 : unsignedNoDecimalMap(H) * 60 + unsignedNoDecimalMap(M);
2147 }
2148 };
2149
2150 /**
2151 * Maps a dateTimeLexicalRep to a dateTime value.
2152 *
2153 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateTimeLexRep>.
2154 *
2155 * ☡ This function throws if the provided value is not a
2156 * dateTimeLexicalRep string.
2157 */
2158 export const dateTimeLexicalMap = (LEX) => {
2159 let Y, MO, D, H, MI, S, T;
2160 new Matcher(
2161 rawString`(?<Y>-?\d+)-(?<MO>\d+)-(?<D>\d+)T(?:24:00:00(?:\.0+)?|(?<H>\d+):(?<MI>\d+):(?<S>[\d.]+))(?<T>.+)?`,
2162 undefined,
2163 (_, { groups }) => {
2164 Y = groups.Y;
2165 MO = groups.MO;
2166 D = groups.D;
2167 H = groups.H ?? absent;
2168 MI = groups.MI ?? absent;
2169 S = groups.S ?? absent;
2170 T = groups.T ?? absent;
2171 return true;
2172 },
2173 )(ensureMatches(dateTimeLexicalRep, LEX));
2174 const tz = T === absent ? absent : timezoneFragValue(T);
2175 return H === absent
2176 ? newDateTime(
2177 yearFragValue(Y),
2178 monthFragValue(MO),
2179 dayFragValue(D),
2180 24,
2181 0,
2182 0,
2183 tz,
2184 )
2185 : newDateTime(
2186 yearFragValue(Y),
2187 monthFragValue(MO),
2188 dayFragValue(D),
2189 hourFragValue(H),
2190 minuteFragValue(MI),
2191 secondFragValue(S),
2192 tz,
2193 );
2194 };
2195
2196 /**
2197 * Maps a timeLexicalMap to a time value.
2198 *
2199 * See <https://www.w3.org/TR/xmlschema11-2/#vp-timeLexRep>.
2200 *
2201 * ☡ This function throws if the provided value is not a timeLexicalMap
2202 * string.
2203 */
2204 export const timeLexicalMap = (LEX) => {
2205 let H, MI, S, T;
2206 new Matcher(
2207 rawString`(?:24:00:00(?:\.0+)?|(?<H>\d+):(?<MI>\d+):(?<S>[\d.]+))(?<T>.+)?`,
2208 undefined,
2209 (_, { groups }) => {
2210 H = groups.H ?? absent;
2211 MI = groups.MI ?? absent;
2212 S = groups.S ?? absent;
2213 T = groups.T ?? absent;
2214 return true;
2215 },
2216 )(ensureMatches(timeLexicalRep, LEX));
2217 const tz = T === absent ? absent : timezoneFragValue(T);
2218 return H === absent
2219 ? newDateTime(
2220 absent,
2221 absent,
2222 absent,
2223 0,
2224 0,
2225 0,
2226 tz,
2227 )
2228 : newDateTime(
2229 absent,
2230 absent,
2231 absent,
2232 hourFragValue(H),
2233 minuteFragValue(MI),
2234 secondFragValue(S),
2235 tz,
2236 );
2237 };
2238
2239 /**
2240 * Maps a dateLexicalRep to a date value.
2241 *
2242 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateLexRep>.
2243 *
2244 * ☡ This function throws if the provided value is not a dateLexicalRep
2245 * string.
2246 */
2247 export const dateLexicalMap = (LEX) => {
2248 let Y, M, D, T;
2249 new Matcher(
2250 rawString`(?<Y>-?\d+)-(?<M>\d+)-(?<D>\d+)(?<T>.+)?`,
2251 undefined,
2252 (_, { groups }) => {
2253 Y = groups.Y;
2254 M = groups.M;
2255 D = groups.D;
2256 T = groups.T ?? absent;
2257 return true;
2258 },
2259 )(ensureMatches(dateLexicalRep, LEX));
2260 const tz = T === absent ? absent : timezoneFragValue(T);
2261 return newDateTime(
2262 yearFragValue(Y),
2263 monthFragValue(M),
2264 dayFragValue(D),
2265 absent,
2266 absent,
2267 absent,
2268 tz,
2269 );
2270 };
2271
2272 /**
2273 * Maps a gYearMonthLexicalRep to a date value.
2274 *
2275 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearMonthLexRep>.
2276 *
2277 * ☡ This function throws if the provided value is not a
2278 * gYearMonthLexicalRep string.
2279 */
2280 export const gYearMonthLexicalMap = (LEX) => {
2281 let Y, M, T;
2282 new Matcher(
2283 rawString`(?<Y>-?\d+)-(?<M>\d+)(?<T>.+)?`,
2284 undefined,
2285 (_, { groups }) => {
2286 Y = groups.Y;
2287 M = groups.M;
2288 T = groups.T ?? absent;
2289 return true;
2290 },
2291 )(ensureMatches(gYearMonthLexicalRep, LEX));
2292 const tz = T === absent ? absent : timezoneFragValue(T);
2293 return newDateTime(
2294 yearFragValue(Y),
2295 monthFragValue(M),
2296 absent,
2297 absent,
2298 absent,
2299 absent,
2300 tz,
2301 );
2302 };
2303
2304 /**
2305 * Maps a gYearLexicalRep to a date value.
2306 *
2307 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearLexRep>.
2308 *
2309 * ☡ This function throws if the provided value is not a
2310 * gYearLexicalRep string.
2311 */
2312 export const gYearLexicalMap = (LEX) => {
2313 let Y, T;
2314 new Matcher(
2315 rawString`(?<Y>-?\d+)(?<T>.+)?`,
2316 undefined,
2317 (_, { groups }) => {
2318 Y = groups.Y;
2319 T = groups.T ?? absent;
2320 return true;
2321 },
2322 )(ensureMatches(gYearLexicalRep, LEX));
2323 const tz = T === absent ? absent : timezoneFragValue(T);
2324 return newDateTime(
2325 yearFragValue(Y),
2326 absent,
2327 absent,
2328 absent,
2329 absent,
2330 absent,
2331 tz,
2332 );
2333 };
2334
2335 /**
2336 * Maps a gMonthDayLexicalRep to a date value.
2337 *
2338 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthDayLexRep>.
2339 *
2340 * ☡ This function throws if the provided value is not a
2341 * gMonthDayLexicalRep string.
2342 */
2343 export const gMonthDayLexicalMap = (LEX) => {
2344 let M, D, T;
2345 new Matcher(
2346 rawString`--(?<M>\d+)-(?<D>\d+)(?<T>.+)?`,
2347 undefined,
2348 (_, { groups }) => {
2349 M = groups.M;
2350 D = groups.D;
2351 T = groups.T ?? absent;
2352 return true;
2353 },
2354 )(ensureMatches(gMonthDayLexicalRep, LEX));
2355 const tz = T === absent ? absent : timezoneFragValue(T);
2356 return newDateTime(
2357 absent,
2358 monthFragValue(M),
2359 dayFragValue(D),
2360 absent,
2361 absent,
2362 absent,
2363 tz,
2364 );
2365 };
2366
2367 /**
2368 * Maps a gDayLexicalRep to a date value.
2369 *
2370 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gDayLexRep>.
2371 *
2372 * ☡ This function throws if the provided value is not a gDayLexicalRep
2373 * string.
2374 */
2375 export const gDayLexicalMap = (LEX) => {
2376 let D, T;
2377 new Matcher(
2378 rawString`---(?<D>\d+)(?<T>.+)?`,
2379 undefined,
2380 (_, { groups }) => {
2381 D = groups.D;
2382 T = groups.T ?? absent;
2383 return true;
2384 },
2385 )(ensureMatches(gDayLexicalRep, LEX));
2386 const tz = T === absent ? absent : timezoneFragValue(T);
2387 return newDateTime(
2388 absent,
2389 absent,
2390 dayFragValue(D),
2391 absent,
2392 absent,
2393 absent,
2394 tz,
2395 );
2396 };
2397
2398 /**
2399 * Maps a gMonthLexicalRep to a date value.
2400 *
2401 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthLexRep>.
2402 *
2403 * ☡ This function throws if the provided value is not a
2404 * gMonthLexicalRep string.
2405 */
2406 export const gMonthLexicalMap = (LEX) => {
2407 let M, T;
2408 new Matcher(
2409 rawString`--(?<M>\d+)(?<T>.+)?`,
2410 undefined,
2411 (_, { groups }) => {
2412 M = groups.M;
2413 T = groups.T ?? absent;
2414 return true;
2415 },
2416 )(ensureMatches(gMonthLexicalRep, LEX));
2417 const tz = T === absent ? absent : timezoneFragValue(T);
2418 return newDateTime(
2419 absent,
2420 monthFragValue(M),
2421 absent,
2422 absent,
2423 absent,
2424 absent,
2425 tz,
2426 );
2427 };
2428
2429 /**
2430 * Maps a nonnegative integer less than 100 onto an unsigned
2431 * always-two-digit numeral.
2432 *
2433 * See <https://www.w3.org/TR/xmlschema11-2/#f-unsTwoDigCanFragMap>.
2434 *
2435 * ☡ This function throws if the provided value is not a nonnegative
2436 * integer less than 100.
2437 *
2438 * ※ This function is not exposed.
2439 */
2440 const unsTwoDigitCanonicalFragmentMap = (i) => {
2441 if (!(isIntegralNumber(i) && i >= 0 && i < 100)) {
2442 throw new TypeError(
2443 `सूत्र: Expected a nonnegative integer less than 100, but got: ${i}.`,
2444 );
2445 } else {
2446 return stringCatenate(digit(div(i, 10)), digit(mod(i, 10)));
2447 }
2448 };
2449
2450 /**
2451 * Maps an integer between -10000 and 10000 onto an always-four-digit numeral.
2452 *
2453 * See <https://www.w3.org/TR/xmlschema11-2/#f-fourDigCanFragMap>.
2454 *
2455 * ☡ This function throws if the provided value is not an integer whose
2456 * absolute value is less than 10000.
2457 *
2458 * ※ This function is not exposed.
2459 */
2460 const fourDigitCanonicalFragmentMap = (i) => {
2461 if (!(isIntegralNumber(i) && abs(i) < 10000)) {
2462 throw new TypeError(
2463 `सूत्र: Expected an integer whose absolute value is less than 10000, but got: ${i}.`,
2464 );
2465 } else {
2466 return i < 0
2467 ? stringCatenate(
2468 "-",
2469 unsTwoDigitCanonicalFragmentMap(div(-i, 100)),
2470 unsTwoDigitCanonicalFragmentMap(mod(-i, 100)),
2471 )
2472 : stringCatenate(
2473 unsTwoDigitCanonicalFragmentMap(div(i, 100)),
2474 unsTwoDigitCanonicalFragmentMap(mod(i, 100)),
2475 );
2476 }
2477 };
2478
2479 /**
2480 * Maps an integer, presumably the ·year· property of a
2481 * date/timeSevenPropertyModel value, onto a yearFrag, part of a
2482 * date/timeSevenPropertyModel's ·lexical representation·.
2483 *
2484 * See <https://www.w3.org/TR/xmlschema11-2/#f-yrCanFragMap>.
2485 *
2486 * ☡ This function throws if the provided value is not an integer.
2487 */
2488 export const yearCanonicalFragmentMap = (y) =>
2489 abs(integer(y)) > 9999
2490 ? noDecimalPtCanonicalMap(y)
2491 : fourDigitCanonicalFragmentMap(y);
2492
2493 /**
2494 * Maps an integer, presumably the ·month· property of a
2495 * date/timeSevenPropertyModel value, onto a monthFrag, part of a
2496 * date/timeSevenPropertyModel's ·lexical representation·.
2497 *
2498 * See <https://www.w3.org/TR/xmlschema11-2/#f-moCanFragMap>.
2499 *
2500 * ☡ This function throws if the provided value is not an integer
2501 * between 1 and 12 inclusive.
2502 */
2503 export const monthCanonicalFragmentMap = (m) => {
2504 if (!(isIntegralNumber(m) && m >= 1 && m <= 12)) {
2505 throw new TypeError(
2506 `सूत्र: Expected an integer between 1 and 12 inclusive, but got: ${m}.`,
2507 );
2508 } else {
2509 return unsTwoDigitCanonicalFragmentMap(m);
2510 }
2511 };
2512
2513 /**
2514 * Maps an integer, presumably the ·day· property of a
2515 * date/timeSevenPropertyModel value, onto a dayFrag, part of a
2516 * date/timeSevenPropertyModel's ·lexical representation·.
2517 *
2518 * See <https://www.w3.org/TR/xmlschema11-2/#f-daCanFragMap>.
2519 *
2520 * ☡ This function throws if the provided value is not an integer
2521 * between 1 and 31 inclusive.
2522 */
2523 export const dayCanonicalFragmentMap = (d) => {
2524 if (!(isIntegralNumber(d) && d >= 1 && d <= 31)) {
2525 throw new TypeError(
2526 `सूत्र: Expected an integer between 1 and 31 inclusive, but got: ${d}.`,
2527 );
2528 } else {
2529 return unsTwoDigitCanonicalFragmentMap(d);
2530 }
2531 };
2532
2533 /**
2534 * Maps an integer, presumably the ·hour· property of a
2535 * date/timeSevenPropertyModel value, onto an hourFrag, part of a
2536 * date/timeSevenPropertyModel's ·lexical representation·.
2537 *
2538 * See <https://www.w3.org/TR/xmlschema11-2/#f-hrCanFragMap>.
2539 *
2540 * ☡ This function throws if the provided value is not an integer
2541 * between 0 and 23 inclusive.
2542 */
2543 export const hourCanonicalFragmentMap = (h) => {
2544 if (!(isIntegralNumber(h) && h >= 0 && h <= 23)) {
2545 throw new TypeError(
2546 `सूत्र: Expected an integer between 0 and 23 inclusive, but got: ${h}.`,
2547 );
2548 } else {
2549 return unsTwoDigitCanonicalFragmentMap(h);
2550 }
2551 };
2552
2553 /**
2554 * Maps an integer, presumably the ·minute· property of a
2555 * date/timeSevenPropertyModel value, onto a minuteFrag, part of a
2556 * date/timeSevenPropertyModel's ·lexical representation·.
2557 *
2558 * See <https://www.w3.org/TR/xmlschema11-2/#f-miCanFragMap>.
2559 *
2560 * ☡ This function throws if the provided value is not an integer
2561 * between 0 and 59 inclusive.
2562 */
2563 export const minuteCanonicalFragmentMap = (m) => {
2564 if (!(isIntegralNumber(m) && m >= 0 && m <= 59)) {
2565 throw new TypeError(
2566 `सूत्र: Expected an integer between 0 and 59 inclusive, but got: ${m}.`,
2567 );
2568 } else {
2569 return unsTwoDigitCanonicalFragmentMap(m);
2570 }
2571 };
2572
2573 /**
2574 * Maps a decimal number, presumably the ·second· property of a
2575 * date/timeSevenPropertyModel value, onto a secondFrag, part of a
2576 * date/timeSevenPropertyModel's ·lexical representation·.
2577 *
2578 * See <https://www.w3.org/TR/xmlschema11-2/#f-seCanFragMap>.
2579 *
2580 * ☡ This function throws if the provided value is not a nonnegative
2581 * finite number less than 60.
2582 *
2583 * ※ The X·S·D specification identifies the argument to this function
2584 * as “a nonnegative decimal number less than 70”, but second values 60
2585 * or larger are not supported by X·S·D.
2586 *
2587 * ※ This function makes use of the built·in Ecmascript float
2588 * serialization to avoid rounding errors.
2589 */
2590 export const secondCanonicalFragmentMap = (s) => {
2591 if (!(isFiniteNumber(s) && s < 60)) {
2592 throw new TypeError(
2593 `सूत्र: Expected a nonnegative finite number less than 60, but got: ${s}.`,
2594 );
2595 } else {
2596 // return isIntegralNumber(s)
2597 // ? unsTwoDigitCanonicalFragmentMap(s)
2598 // : stringCatenate(
2599 // unsTwoDigitCanonicalFragmentMap(div(s, 1)),
2600 // ".",
2601 // fractionDigitsCanonicalFragmentMap(mod(s, 1)),
2602 // );
2603 return isIntegralNumber(s)
2604 ? unsTwoDigitCanonicalFragmentMap(s)
2605 : s < 10
2606 ? stringCatenate("0", decimalPtCanonicalMap(s))
2607 : decimalPtCanonicalMap(s);
2608 }
2609 };
2610
2611 /**
2612 * Maps an integer, presumably the ·timezoneOffset· property of a
2613 * date/timeSevenPropertyModel value, onto a timezoneFrag, part of a
2614 * date/timeSevenPropertyModel's ·lexical representation·.
2615 *
2616 * See <https://www.w3.org/TR/xmlschema11-2/#f-tzCanFragMap>.
2617 *
2618 * ☡ This function throws if the provided value is not an integer
2619 * between −840 and 840 inclusive.
2620 */
2621 export const timezoneCanonicalFragmentMap = (t) => {
2622 if (!(isIntegralNumber(t) && t >= -840 && t <= 840)) {
2623 throw new TypeError(
2624 `सूत्र: Expected an integer between −840 and 840 inclusive, but got: ${t}.`,
2625 );
2626 } else {
2627 return t === 0 ? "Z" : t < 0
2628 ? stringCatenate(
2629 "-",
2630 unsTwoDigitCanonicalFragmentMap(div(-t, 60)),
2631 ":",
2632 unsTwoDigitCanonicalFragmentMap(mod(-t, 60)),
2633 )
2634 : stringCatenate(
2635 "+",
2636 unsTwoDigitCanonicalFragmentMap(div(t, 60)),
2637 ":",
2638 unsTwoDigitCanonicalFragmentMap(mod(t, 60)),
2639 );
2640 }
2641 };
2642
2643 /**
2644 * Maps a dateTime value to a dateTimeLexicalRep.
2645 *
2646 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateTimeCanRep>.
2647 *
2648 * ☡ This function throws if the provided value is not a complete
2649 * dateTime.
2650 */
2651 export const dateTimeCanonicalMap = (dt) => {
2652 const {
2653 year,
2654 month,
2655 day,
2656 hour,
2657 minute,
2658 second,
2659 timezoneOffset,
2660 } = date·timeSevenPropertyModel(dt);
2661 ensurePresence("year", year, true);
2662 ensurePresence("month", month, true);
2663 ensurePresence("day", day, true);
2664 ensurePresence("hour", hour, true);
2665 ensurePresence("minute", minute, true);
2666 ensurePresence("second", second, true);
2667 const DT = stringCatenate(
2668 yearCanonicalFragmentMap(year),
2669 "-",
2670 monthCanonicalFragmentMap(month),
2671 "-",
2672 dayCanonicalFragmentMap(day),
2673 "T",
2674 hourCanonicalFragmentMap(hour),
2675 ":",
2676 minuteCanonicalFragmentMap(minute),
2677 ":",
2678 secondCanonicalFragmentMap(second),
2679 );
2680 return timezoneOffset === absent
2681 ? DT
2682 : stringCatenate(DT, timezoneCanonicalFragmentMap(timezoneOffset));
2683 };
2684
2685 /**
2686 * Maps a time value to a timeLexicalRep.
2687 *
2688 * See <https://www.w3.org/TR/xmlschema11-2/#vp-timeCanRep>.
2689 *
2690 * ☡ This function throws if the provided value is not a complete time.
2691 */
2692 export const timeCanonicalMap = (ti) => {
2693 const {
2694 year,
2695 month,
2696 day,
2697 hour,
2698 minute,
2699 second,
2700 timezoneOffset,
2701 } = date·timeSevenPropertyModel(ti);
2702 ensurePresence("year", year, false);
2703 ensurePresence("month", month, false);
2704 ensurePresence("day", day, false);
2705 ensurePresence("hour", hour, true);
2706 ensurePresence("minute", minute, true);
2707 ensurePresence("second", second, true);
2708 const T = stringCatenate(
2709 hourCanonicalFragmentMap(hour),
2710 ":",
2711 minuteCanonicalFragmentMap(minute),
2712 ":",
2713 secondCanonicalFragmentMap(second),
2714 );
2715 return timezoneOffset === absent
2716 ? T
2717 : stringCatenate(T, timezoneCanonicalFragmentMap(timezoneOffset));
2718 };
2719
2720 /**
2721 * Maps a date value to a dateLexicalRep.
2722 *
2723 * See <https://www.w3.org/TR/xmlschema11-2/#vp-dateCanRep>.
2724 *
2725 * ☡ This function throws if the provided value is not a complete date.
2726 */
2727 export const dateCanonicalMap = (da) => {
2728 const {
2729 year,
2730 month,
2731 day,
2732 hour,
2733 minute,
2734 second,
2735 timezoneOffset,
2736 } = date·timeSevenPropertyModel(da);
2737 ensurePresence("year", year, true);
2738 ensurePresence("month", month, true);
2739 ensurePresence("day", day, true);
2740 ensurePresence("hour", hour, false);
2741 ensurePresence("minute", minute, false);
2742 ensurePresence("second", second, false);
2743 const D = stringCatenate(
2744 yearCanonicalFragmentMap(year),
2745 "-",
2746 monthCanonicalFragmentMap(month),
2747 "-",
2748 dayCanonicalFragmentMap(day),
2749 );
2750 return timezoneOffset === absent
2751 ? D
2752 : stringCatenate(D, timezoneCanonicalFragmentMap(timezoneOffset));
2753 };
2754
2755 /**
2756 * Maps a gYearMonth value to a gYearMonthLexicalRep.
2757 *
2758 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearMonthCanRep>.
2759 *
2760 * ☡ This function throws if the provided value is not a complete
2761 * gYearMonth.
2762 */
2763 export const gYearMonthCanonicalMap = (ym) => {
2764 const {
2765 year,
2766 month,
2767 day,
2768 hour,
2769 minute,
2770 second,
2771 timezoneOffset,
2772 } = date·timeSevenPropertyModel(ym);
2773 ensurePresence("year", year, true);
2774 ensurePresence("month", month, true);
2775 ensurePresence("day", day, false);
2776 ensurePresence("hour", hour, false);
2777 ensurePresence("minute", minute, false);
2778 ensurePresence("second", second, false);
2779 const YM = stringCatenate(
2780 yearCanonicalFragmentMap(year),
2781 "-",
2782 monthCanonicalFragmentMap(month),
2783 );
2784 return timezoneOffset === absent
2785 ? YM
2786 : stringCatenate(YM, timezoneCanonicalFragmentMap(timezoneOffset));
2787 };
2788
2789 /**
2790 * Maps a gYear value to a gYearLexicalRep.
2791 *
2792 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gYearCanRep>.
2793 *
2794 * ☡ This function throws if the provided value is not a complete
2795 * gYear.
2796 */
2797 export const gYearCanonicalMap = (gY) => {
2798 const {
2799 year,
2800 month,
2801 day,
2802 hour,
2803 minute,
2804 second,
2805 timezoneOffset,
2806 } = date·timeSevenPropertyModel(gY);
2807 ensurePresence("year", year, true);
2808 ensurePresence("month", month, false);
2809 ensurePresence("day", day, false);
2810 ensurePresence("hour", hour, false);
2811 ensurePresence("minute", minute, false);
2812 ensurePresence("second", second, false);
2813 return timezoneOffset === absent
2814 ? yearCanonicalFragmentMap(year)
2815 : stringCatenate(
2816 yearCanonicalFragmentMap(year),
2817 timezoneCanonicalFragmentMap(timezoneOffset),
2818 );
2819 };
2820
2821 /**
2822 * Maps a gMonthDay value to a gMonthDayLexicalRep.
2823 *
2824 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthDayCanRep>.
2825 *
2826 * ☡ This function throws if the provided value is not a complete
2827 * gMonthDay.
2828 */
2829 export const gMonthDayCanonicalMap = (md) => {
2830 const {
2831 year,
2832 month,
2833 day,
2834 hour,
2835 minute,
2836 second,
2837 timezoneOffset,
2838 } = date·timeSevenPropertyModel(md);
2839 ensurePresence("year", year, false);
2840 ensurePresence("month", month, true);
2841 ensurePresence("day", day, true);
2842 ensurePresence("hour", hour, false);
2843 ensurePresence("minute", minute, false);
2844 ensurePresence("second", second, false);
2845 const MD = stringCatenate(
2846 "--",
2847 monthCanonicalFragmentMap(month),
2848 "-",
2849 dayCanonicalFragmentMap(day),
2850 );
2851 return timezoneOffset === absent
2852 ? MD
2853 : stringCatenate(MD, timezoneCanonicalFragmentMap(timezoneOffset));
2854 };
2855
2856 /**
2857 * Maps a gDay value to a gDayLexicalRep.
2858 *
2859 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gDayCanRep>.
2860 *
2861 * ☡ This function throws if the provided value is not a complete gDay.
2862 */
2863 export const gDayCanonicalMap = (gD) => {
2864 const {
2865 year,
2866 month,
2867 day,
2868 hour,
2869 minute,
2870 second,
2871 timezoneOffset,
2872 } = date·timeSevenPropertyModel(gD);
2873 ensurePresence("year", year, false);
2874 ensurePresence("month", month, false);
2875 ensurePresence("day", day, true);
2876 ensurePresence("hour", hour, false);
2877 ensurePresence("minute", minute, false);
2878 ensurePresence("second", second, false);
2879 return timezoneOffset === absent
2880 ? stringCatenate("---", dayCanonicalFragmentMap(day))
2881 : stringCatenate(
2882 "---",
2883 dayCanonicalFragmentMap(day),
2884 timezoneCanonicalFragmentMap(timezoneOffset),
2885 );
2886 };
2887
2888 /**
2889 * Maps a gMonth value to a gMonthLexicalRep.
2890 *
2891 * See <https://www.w3.org/TR/xmlschema11-2/#vp-gMonthCanRep>.
2892 *
2893 * ☡ This function throws if the provided value is not a complete
2894 * gMonth.
2895 */
2896 export const gMonthCanonicalMap = (gM) => {
2897 const {
2898 year,
2899 month,
2900 day,
2901 hour,
2902 minute,
2903 second,
2904 timezoneOffset,
2905 } = date·timeSevenPropertyModel(gM);
2906 ensurePresence("year", year, false);
2907 ensurePresence("month", month, true);
2908 ensurePresence("day", day, false);
2909 ensurePresence("hour", hour, false);
2910 ensurePresence("minute", minute, false);
2911 ensurePresence("second", second, false);
2912 return timezoneOffset === absent
2913 ? stringCatenate("--", dayCanonicalFragmentMap(month))
2914 : stringCatenate(
2915 "--",
2916 dayCanonicalFragmentMap(month),
2917 timezoneCanonicalFragmentMap(timezoneOffset),
2918 );
2919 };
2920
2921 /**
2922 * Maps a ·literal· matching the stringRep production to a string
2923 * value.
2924 *
2925 * See <https://www.w3.org/TR/xmlschema11-2/#f-stringLexmap>.
2926 *
2927 * ☡ This function throws if the provided value is not a stringRep
2928 * string.
2929 */
2930 export const stringLexicalMap = (LEX) => ensureMatches(stringRep, LEX);
2931
2932 /**
2933 * Maps a ·literal· matching the booleanRep production to a boolean
2934 * value.
2935 *
2936 * See <https://www.w3.org/TR/xmlschema11-2/#f-booleanLexmap>.
2937 *
2938 * ☡ This function throws if the provided value is not a booleanRep
2939 * string.
2940 */
2941 export const booleanLexicalMap = (LEX) => {
2942 ensureMatches(booleanRep, LEX);
2943 return LEX === "true" || LEX === "1";
2944 };
2945
2946 /**
2947 * Maps a string value to a stringRep.
2948 *
2949 * See <https://www.w3.org/TR/xmlschema11-2/#f-stringCanmap>.
2950 *
2951 * ☡ This function throws if the provided value is not a valid string.
2952 */
2953 export const stringCanonicalMap = (s) => ensureMatches(stringRep, s);
2954
2955 /**
2956 * Maps a boolean value to a booleanRep.
2957 *
2958 * See <https://www.w3.org/TR/xmlschema11-2/#f-booleanCanmap>.
2959 *
2960 * ☡ This function throws if the provided value is not a boolean.
2961 */
2962 export const booleanCanonicalMap = (b) => {
2963 if (typeof b !== "boolean") {
2964 throw new TypeError(`सूत्र: Expected a boolean, but got: ${b}.`);
2965 } else {
2966 return b ? "true" : "false";
2967 }
2968 };
2969
2970 /**
2971 * Maps a ·literal· matching the hexBinary production to a sequence of
2972 * octets in the form of a hexBinary value.
2973 *
2974 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexBinaryMap>.
2975 *
2976 * ☡ This function throws if the provided value is not a hexBinary
2977 * string.
2978 *
2979 * ※ This function returns an `ArrayBuffer`.
2980 */
2981 export const hexBinaryMap = (LEX) => {
2982 ensureMatches(hexBinary, LEX);
2983 const bufferLength = LEX.length / 2;
2984 const o = new ArrayBuffer(bufferLength);
2985 for (let bufferIndex = 0, index = 0; bufferIndex < bufferLength;) {
2986 set8BitIntegralItem(
2987 o,
2988 bufferIndex,
2989 hexOctetMap(substring(LEX, index, index = ++bufferIndex * 2)),
2990 );
2991 }
2992 return o;
2993 };
2994
2995 /**
2996 * Maps a ·literal· matching the hexOctet production to a single octet.
2997 *
2998 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexOctetMap>.
2999 *
3000 * ☡ This function throws if the provided value is not a hexOctet
3001 * string.
3002 *
3003 * ※ This function is not exposed.
3004 */
3005 const hexOctetMap = (LEX) => {
3006 const [d0, d1] = ensureMatches(hexOctet, LEX);
3007 let octet = 0;
3008 let bit = 8;
3009 for (const digit of hexDigitMap(d0)) {
3010 octet |= digit << --bit;
3011 }
3012 for (const digit of hexDigitMap(d1)) {
3013 octet |= digit << --bit;
3014 }
3015 return octet;
3016 };
3017
3018 /**
3019 * Maps a hexadecimal digit (a character matching the hexDigit
3020 * production) to a sequence of four binary digits.
3021 *
3022 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexDigitMap>.
3023 *
3024 * ☡ This function throws if the provided value is not a hexDigit
3025 * string.
3026 *
3027 * ※ It would be way simpler and more efficient to just return a
3028 * single value here rather than a sequence of digits.
3029 *
3030 * ※ This function is not exposed.
3031 */
3032 const hexDigitMap = (d) => {
3033 switch (ensureMatches(hexDigit, d)) {
3034 case "0":
3035 return binaryDigitSequence("0000");
3036 case "1":
3037 return binaryDigitSequence("0001");
3038 case "2":
3039 return binaryDigitSequence("0010");
3040 case "3":
3041 return binaryDigitSequence("0011");
3042 case "4":
3043 return binaryDigitSequence("0100");
3044 case "5":
3045 return binaryDigitSequence("0101");
3046 case "6":
3047 return binaryDigitSequence("0110");
3048 case "7":
3049 return binaryDigitSequence("0111");
3050 case "8":
3051 return binaryDigitSequence("1000");
3052 case "9":
3053 return binaryDigitSequence("1001");
3054 case "A":
3055 case "a":
3056 return binaryDigitSequence("1010");
3057 case "B":
3058 case "b":
3059 return binaryDigitSequence("1011");
3060 case "C":
3061 case "c":
3062 return binaryDigitSequence("1100");
3063 case "D":
3064 case "d":
3065 return binaryDigitSequence("1101");
3066 case "E":
3067 case "e":
3068 return binaryDigitSequence("1110");
3069 case "F":
3070 case "f":
3071 return binaryDigitSequence("1111");
3072 }
3073 };
3074
3075 /**
3076 * Maps a hexBinary value to a literal matching the hexBinary
3077 * production.
3078 *
3079 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexBinaryCanonical>.
3080 *
3081 * ☡ This function throws if the provided value is not an
3082 * `ArrayBuffer`.
3083 */
3084 export const hexBinaryCanonical = (o) => {
3085 hexBinaryValue(o);
3086 const length = getByteLength(o);
3087 return stringCatenate(
3088 "",
3089 ...generatorSequence(function* () {
3090 for (let index = 0; index < length; ++index) {
3091 yield hexOctetCanonical(
3092 toNumber(get8BitUnsignedIntegralItem(o, index)),
3093 );
3094 }
3095 }),
3096 );
3097 };
3098
3099 /**
3100 * Maps a binary octet to a literal matching the hexOctet production.
3101 *
3102 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexOctetCanonical>.
3103 *
3104 * ☡ This function throws if the provided value is not a hexOctet
3105 * string.
3106 *
3107 * ※ This function is not exposed.
3108 */
3109 const hexOctetCanonical = (o) => {
3110 if (!(isIntegralNumber(o) && o >= 0 && o <= 0xFF)) {
3111 throw new TypeError(
3112 `सूत्र: Expected a binary octet but got: ${o}.`,
3113 );
3114 } else {
3115 const lo = generatorSequence(function* () {
3116 for (let i = 1; i <= 4; ++i) {
3117 const offset = 4 - i;
3118 yield (o & 1 << offset) >> offset;
3119 }
3120 });
3121 const hi = generatorSequence(function* () {
3122 for (let i = 1; i <= 4; ++i) {
3123 const offset = 8 - i;
3124 yield (o & 1 << offset) >> offset;
3125 }
3126 });
3127 return hexDigitCanonical(hi) + hexDigitCanonical(lo);
3128 }
3129 };
3130
3131 /**
3132 * Maps a four-bit sequence to a hexadecimal digit (a literal matching
3133 * the hexDigit production).
3134 *
3135 * See <https://www.w3.org/TR/xmlschema11-2/#f-hexDigitCanonical>.
3136 *
3137 * ☡ This function throws if the provided value is not an iterable of
3138 * binary digits.
3139 *
3140 * ※ It would be way simpler and more efficient to just call this with
3141 * a single value rather than a sequence of digits.
3142 *
3143 * ※ This function is not exposed.
3144 */
3145 const hexDigitCanonical = (d) => {
3146 let sum = 0;
3147 for (const digit of d) {
3148 if (digit != 0 && digit != 1) {
3149 throw new TypeError(
3150 `सूत्र: Expected a binary digit but got: ${digit}.`,
3151 );
3152 } else {
3153 sum = (sum << 1) | digit;
3154 }
3155 }
3156 switch (sum) {
3157 case 0b0000:
3158 return "0";
3159 case 0b0001:
3160 return "1";
3161 case 0b0010:
3162 return "2";
3163 case 0b0011:
3164 return "3";
3165 case 0b0100:
3166 return "4";
3167 case 0b0101:
3168 return "5";
3169 case 0b0110:
3170 return "6";
3171 case 0b0111:
3172 return "7";
3173 case 0b1000:
3174 return "8";
3175 case 0b1001:
3176 return "9";
3177 case 0b1010:
3178 return "A";
3179 case 0b1011:
3180 return "B";
3181 case 0b1100:
3182 return "C";
3183 case 0b1101:
3184 return "D";
3185 case 0b1110:
3186 return "E";
3187 case 0b1111:
3188 return "F";
3189 }
3190 };
This page took 0.661952 seconds and 5 git commands to generate.