]> Lady’s Gitweb - Sutra/blob - xsd/functions.js
Initial commit
[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 bind,
12 call,
13 getPrototype,
14 isArrayBuffer,
15 isFiniteNumber,
16 isIntegralNumber,
17 ITERATOR,
18 objectCreate,
19 sameValue,
20 toFloat32,
21 type,
22 } from "../deps.js";
23 import { div, mod } from "./operators.js";
24 import {
25 booleanRep,
26 dateLexicalRep,
27 dateTimeLexicalRep,
28 dayFrag,
29 dayTimeDurationLexicalRep,
30 decimalLexicalRep,
31 decimalPtNumeral,
32 digit as lexicalDigit,
33 doubleRep,
34 duDayFrag,
35 duDayTimeFrag,
36 duHourFrag,
37 duMinuteFrag,
38 duMonthFrag,
39 durationLexicalRep,
40 duSecondFrag,
41 duTimeFrag,
42 duYearFrag,
43 duYearMonthFrag,
44 floatRep,
45 fracFrag,
46 gDayLexicalRep,
47 gMonthDayLexicalRep,
48 gMonthLexicalRep,
49 gYearLexicalRep,
50 gYearMonthLexicalRep,
51 hexBinary,
52 hexDigit,
53 hexOctet,
54 hourFrag,
55 minuteFrag,
56 monthFrag,
57 noDecimalPtNumeral,
58 numericalSpecialRep,
59 scientificNotationNumeral,
60 secondFrag,
61 stringRep,
62 timeLexicalRep,
63 timezoneFrag,
64 unsignedDecimalPtNumeral,
65 unsignedNoDecimalPtNumeral,
66 yearFrag,
67 yearMonthDurationLexicalRep,
68 } from "./productions.js";
69 import {
70 absent,
71 negativeInfinity,
72 negativeZero,
73 notANumber,
74 positiveInfinity,
75 positiveZero,
76 } from "./values.js";
77
78 const stringToFloat = parseFloat;
79
80 /**
81 * Ensures that the provided matcher matches the provided value and
82 * returns it.
83 *
84 * ☡ This function throws if the matcher does not match.
85 *
86 * ※ This function is not exposed.
87 */
88 const ensureMatches = (matcher, $) => {
89 if (matcher($)) {
90 return $;
91 } else {
92 throw new TypeError(
93 `सूत्र: Expected a ${matcher.name}, but got: ${$}.`,
94 );
95 }
96 };
97
98 /**
99 * Ensures that the provided value with the provided name is present or
100 * absent.
101 *
102 * ☡ This function throws if the presence of the value is not correct.
103 *
104 * ※ This function is not exposed.
105 */
106 const ensurePresence = (name, value, present = true) => {
107 if ((value !== absent) === !present) {
108 throw new TypeError(
109 `सूत्र: Expected ${name} to be ${
110 present ? "present" : "absent"
111 }, but got: ${value}.`,
112 );
113 } else {
114 return value;
115 }
116 };
117
118 /**
119 * Ensures that the provided value is a valid, complete duration and
120 * returns an object with its months and seconds.
121 *
122 * ☡ This function throws if the provided value is not a valid,
123 * complete duration.
124 *
125 * ※ This function is not exposed.
126 */
127 const duration = ($) => {
128 const { months, seconds } = $;
129 if (!isIntegralNumber(months)) {
130 throw new TypeError(
131 `सूत्र: Expected duration to have an integer for months, but got: ${months}.`,
132 );
133 } else if (!isFiniteNumber(seconds)) {
134 throw new TypeError(
135 `सूत्र: Expected duration to have a finite number for seconds, but got: ${seconds}.`,
136 );
137 } else if (months > 0 && seconds < 0 || months < 0 && seconds > 0) {
138 throw new TypeError(
139 `सूत्र: Expected seconds in duration to match polarity of months, but got: ${seconds}.`,
140 );
141 } else {
142 return { months, seconds };
143 }
144 };
145
146 /**
147 * Ensures that the provided value is a valid
148 * date/timeSevenPropertyModel value and returns an object with its
149 * year, month, day, hour, minute, second, and timezoneOffset.
150 *
151 * ☡ This function throws if the provided value is not a valid
152 * date/timeSevenPropertyModel value.
153 *
154 * ※ This function is not exposed.
155 */
156 const date·timeSevenPropertyModel = ($) => {
157 if (type($) !== "object") {
158 throw new TypeError(
159 `सूत्र: Expected a date/timeSevenPropertyModel value, but got: ${$}.`,
160 );
161 } else {
162 const { year, month, day, hour, minute, second, timezoneOffset } =
163 $;
164 if (year !== absent && !isIntegralNumber(year)) {
165 throw new TypeError(
166 `सूत्र: Expected year to be an optional integer, but got: ${year}.`,
167 );
168 } else if (
169 month !== absent &&
170 !(isIntegralNumber(month) && month >= 1 && month <= 12)
171 ) {
172 throw new TypeError(
173 `सूत्र: Expected month to be an optional integer between 1 and 12 inclusive, but got: ${month}.`,
174 );
175 } else if (
176 day !== absent &&
177 !(isIntegralNumber(day) && day >= 1 && day <= 31)
178 ) {
179 throw new TypeError(
180 `सूत्र: Expected day to be an optional integer between 1 and 31 inclusive, but got: ${day}.`,
181 );
182 } else if (
183 hour !== absent &&
184 !(isIntegralNumber(hour) && hour >= 0 && hour <= 24)
185 ) {
186 throw new TypeError(
187 `सूत्र: Expected hour to be an optional integer between 0 and 24 inclusive, but got: ${hour}.`,
188 );
189 } else if (
190 minute !== absent &&
191 !(isIntegralNumber(minute) && minute >= 0 && minute <= 59)
192 ) {
193 throw new TypeError(
194 `सूत्र: Expected minute to be an optional integer between 0 and 59 inclusive, but got: ${minute}.`,
195 );
196 } else if (
197 second !== absent &&
198 !(isFiniteNumber(second) && second >= 0 && second < 60)
199 ) {
200 throw new TypeError(
201 `सूत्र: Expected second to be an optional finite number greater than or equal to 0 and less than 60, but got: ${second}.`,
202 );
203 } else if (
204 timezoneOffset !== absent &&
205 !(isIntegralNumber(timezoneOffset) && timezoneOffset >= -840 &&
206 timezoneOffset <= 840)
207 ) {
208 throw new TypeError(
209 `सूत्र: Expected timezoneOffset to be an optional integer between -840 and 840 inclusive, but got: ${timezoneOffset}.`,
210 );
211 } else if (
212 month !== absent && day !== absent && !(
213 month === 2 && day === 29
214 ) && day > daysInMonth(year, month)
215 ) {
216 throw new TypeError(
217 `सूत्र: The provided day violates the day‐of‐month constraint for month ${month}: ${day}.`,
218 );
219 } else {
220 return {
221 year,
222 month,
223 day,
224 hour,
225 minute,
226 second,
227 timezoneOffset,
228 };
229 }
230 }
231 };
232
233 /**
234 * Ensures that the provided value is an integer and returns it.
235 *
236 * ☡ This function throws if the provided value is not an integer.
237 *
238 * ※ This function is not exposed.
239 */
240 const integer = ($) => {
241 if (!isIntegralNumber($)) {
242 throw new TypeError(`सूत्र: Expected an integer, but got: ${$}.`);
243 } else {
244 return $;
245 }
246 };
247
248 /**
249 * Ensures that the provided value is a nonnegative integer and returns
250 * it.
251 *
252 * ☡ This function throws if the provided value is not a nonnegative
253 * integer.
254 *
255 * ※ This function is not exposed.
256 */
257 const nonnegativeInteger = ($) => {
258 if (!(isIntegralNumber($) && $ >= 0)) {
259 throw new TypeError(
260 `सूत्र: Expected a nonnegative integer, but got: ${$}.`,
261 );
262 } else {
263 return $;
264 }
265 };
266
267 /**
268 * Ensures that the provided value is a finite number and returns it.
269 *
270 * ☡ This function throws if the provided value is not a finite number.
271 *
272 * ※ This function is not exposed.
273 */
274 const decimalNumber = ($) => {
275 if (!(isFiniteNumber($))) {
276 throw new TypeError(
277 `सूत्र: Expected a finite number, but got: ${$}.`,
278 );
279 } else {
280 return $;
281 }
282 };
283
284 /**
285 * Ensures that the provided value is a nonnegative finite number and
286 * returns it.
287 *
288 * ☡ This function throws if the provided value is not a nonnegative
289 * finite number.
290 *
291 * ※ This function is not exposed.
292 */
293 const nonnegativeDecimalNumber = ($) => {
294 if (!(isFiniteNumber($) && $ >= 0)) {
295 throw new TypeError(
296 `सूत्र: Expected a nonnegative finite number, but got: ${$}.`,
297 );
298 } else {
299 return $;
300 }
301 };
302
303 /**
304 * Ensures that the provided value is a 32‐bit float and returns it.
305 *
306 * ☡ This function throws if the provided value is not a 32‐bit float.
307 *
308 * ※ This function is not exposed.
309 */
310 const float = ($) => {
311 if (typeof $ !== "number" || !sameValue(toFloat32($), $)) {
312 throw new TypeError(
313 `सूत्र: Expected a float value, but got: ${$}.`,
314 );
315 } else {
316 return $;
317 }
318 };
319
320 /**
321 * Ensures that the provided value is a number and returns it.
322 *
323 * ☡ This function throws if the provided value is not a number.
324 *
325 * ※ This function is not exposed.
326 */
327 const double = ($) => {
328 if (typeof $ !== "number") {
329 throw new TypeError(
330 `सूत्र: Expected a double value, but got: ${$}.`,
331 );
332 } else {
333 return $;
334 }
335 };
336
337 /**
338 * Ensures that the provided value is a hexBinary value, i·e a sequence
339 * of binary octets.
340 *
341 * ☡ This function throws if the provided value is not an array buffer.
342 *
343 * ※ This function is not exposed.
344 */
345 const hexBinaryValue = ($) => {
346 if (!isArrayBuffer($)) {
347 throw new TypeError(
348 `सूत्र: Expected a hexBinary value, but got: ${$}.`,
349 );
350 } else {
351 return $;
352 }
353 };
354
355 const {
356 /**
357 * Converts a string of 0’s and 1’s into a sequence of binary digits.
358 *
359 * ※ This function is not exposed.
360 */
361 binaryDigitSequence,
362
363 /**
364 * Converts a generator iterator into a sequence of values.
365 *
366 * ※ This function is not exposed.
367 */
368 generatorSequence,
369
370 /**
371 * Converts a string into a sequence of characters.
372 *
373 * ※ This function is not exposed.
374 */
375 literalSequence,
376 } = (() => {
377 const {
378 next: generatorIteratorNext,
379 } = getPrototype(function* () {}.prototype);
380 const { [ITERATOR]: stringIterator } = String.prototype;
381 const {
382 next: stringIteratorNext,
383 } = getPrototype(""[ITERATOR]());
384 const binaryDigitIterator = function* () {
385 for (const digit of literalSequence(this)) {
386 yield digit === "0" ? 0 : 1;
387 }
388 };
389 const binaryDigitSequenceIterablePrototype = {
390 [ITERATOR]() {
391 return {
392 next: bind(
393 generatorIteratorNext,
394 call(binaryDigitIterator, this.binaryDigits, []),
395 [],
396 ),
397 };
398 },
399 };
400 const generatorSequenceIterablePrototype = {
401 [ITERATOR]() {
402 return {
403 next: bind(
404 generatorIteratorNext,
405 this.generator(),
406 [],
407 ),
408 };
409 },
410 };
411 const literalSequenceIterablePrototype = {
412 [ITERATOR]() {
413 return {
414 next: bind(
415 stringIteratorNext,
416 call(stringIterator, this.literal, []),
417 [],
418 ),
419 };
420 },
421 };
422
423 return {
424 binaryDigitSequence: (digits) =>
425 objectCreate(
426 binaryDigitSequenceIterablePrototype,
427 { binaryDigits: { value: digits } },
428 ),
429 generatorSequence: (generator) =>
430 objectCreate(
431 generatorSequenceIterablePrototype,
432 { generator: { value: generator } },
433 ),
434 literalSequence: (literal) =>
435 objectCreate(
436 literalSequenceIterablePrototype,
437 { literal: { value: literal } },
438 ),
439 };
440 })();
This page took 0.0833 seconds and 5 git commands to generate.