functionalscript 0.0.520 → 0.0.522

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,499 @@
1
+ const operator = require('../../types/function/operator/module.f.cjs')
2
+ const range_map = require('../../types/range_map/module.f.cjs')
3
+ const { merge, fromRange, get } = range_map
4
+ const list = require('../../types/list/module.f.cjs')
5
+ const _range = require('../../types/range/module.f.cjs')
6
+ const { one } = _range
7
+ const { empty, stateScan, flat, toArray, reduce: listReduce, scan } = list
8
+ const bigfloat = require('../../types/bigfloat/module.f.cjs')
9
+ const jsonTokenizer = require('../../json/tokenizer/module.f.cjs')
10
+ const { fromCharCode } = String
11
+ const {
12
+ range,
13
+ //
14
+ backspace,
15
+ ht,
16
+ lf,
17
+ ff,
18
+ cr,
19
+ //
20
+ space,
21
+ quotationMark,
22
+ plusSign,
23
+ comma,
24
+ hyphenMinus,
25
+ fullStop,
26
+ solidus,
27
+ //
28
+ digitRange,
29
+ digit0,
30
+ colon,
31
+ //
32
+ latinCapitalLetterRange,
33
+ latinCapitalLetterA,
34
+ latinCapitalLetterE,
35
+ //
36
+ leftSquareBracket,
37
+ reverseSolidus,
38
+ rightSquareBracket,
39
+ lowLine,
40
+ //
41
+ latinSmallLetterRange,
42
+ latinSmallLetterA,
43
+ latinSmallLetterB,
44
+ latinSmallLetterE,
45
+ latinSmallLetterF,
46
+ latinSmallLetterN,
47
+ latinSmallLetterR,
48
+ latinSmallLetterT,
49
+ latinSmallLetterU,
50
+ //
51
+ leftCurlyBracket,
52
+ rightCurlyBracket,
53
+ dollarSign
54
+ } = require('../../text/ascii/module.f.cjs')
55
+
56
+ /**
57
+ * @typedef {{
58
+ * readonly kind: 'id'
59
+ * readonly value: string
60
+ * }} IdToken
61
+ * */
62
+
63
+ /**
64
+ * @typedef {|
65
+ * jsonTokenizer.JsonToken |
66
+ * IdToken
67
+ * } FjsonToken
68
+ */
69
+
70
+ const rangeOneNine = range('19')
71
+
72
+ const rangeSetWhiteSpace = [
73
+ one(ht),
74
+ one(lf),
75
+ one(cr),
76
+ one(space)
77
+ ]
78
+
79
+ const rangeSetTerminalForNumber = [
80
+ one(ht),
81
+ one(lf),
82
+ one(cr),
83
+ one(space),
84
+ one(quotationMark),
85
+ one(comma),
86
+ one(leftCurlyBracket),
87
+ one(rightCurlyBracket),
88
+ one(leftSquareBracket),
89
+ one(rightSquareBracket),
90
+ one(colon)
91
+ ]
92
+
93
+ const rangeSmallAF = range('af')
94
+ const rangeCapitalAF = range('AF')
95
+
96
+ const rangeIdStart = [
97
+ latinSmallLetterRange,
98
+ latinCapitalLetterRange,
99
+ one(lowLine),
100
+ one(dollarSign)
101
+ ]
102
+
103
+ const rangeId = [digitRange, ...rangeIdStart]
104
+
105
+ /**
106
+ * @typedef {|
107
+ * InitialState |
108
+ * ParseIdState |
109
+ * ParseStringState |
110
+ * ParseEscapeCharState |
111
+ * ParseUnicodeCharState |
112
+ * ParseNumberState |
113
+ * InvalidNumberState |
114
+ * EofState
115
+ * } TokenizerState
116
+ */
117
+
118
+ /**
119
+ * @typedef {|
120
+ * 'invalid keyword' |
121
+ * '" are missing' |
122
+ * 'unescaped character' |
123
+ * 'invalid hex value' |
124
+ * 'unexpected character' |
125
+ * 'invalid number' |
126
+ * 'eof'
127
+ * } ErrorMessage
128
+ */
129
+
130
+ /** @typedef {{ readonly kind: 'initial'}} InitialState */
131
+
132
+ /** @typedef {{ readonly kind: 'id', readonly value: string}} ParseIdState */
133
+
134
+ /** @typedef {{ readonly kind: 'string', readonly value: string}} ParseStringState */
135
+
136
+ /** @typedef {{ readonly kind: 'escapeChar', readonly value: string}} ParseEscapeCharState */
137
+
138
+ /**
139
+ * @typedef {{
140
+ * readonly kind: 'unicodeChar'
141
+ * readonly value: string
142
+ * readonly unicode: number
143
+ * readonly hexIndex: number
144
+ * }} ParseUnicodeCharState
145
+ */
146
+
147
+ /**
148
+ * @typedef {{
149
+ * readonly kind: 'number',
150
+ * readonly numberKind: '0' | '-' | 'int' | '.' | 'fractional' | 'e' | 'e+' | 'e-' | 'expDigits'
151
+ * readonly value: string
152
+ * readonly b: ParseNumberBuffer
153
+ * }} ParseNumberState
154
+ */
155
+
156
+ /**
157
+ * @typedef {{
158
+ * readonly s: -1n | 1n
159
+ * readonly m: bigint
160
+ * readonly f: number
161
+ * readonly es: -1 | 1
162
+ * readonly e: number
163
+ * }} ParseNumberBuffer
164
+ */
165
+
166
+ /** @typedef {{ readonly kind: 'invalidNumber'}} InvalidNumberState */
167
+
168
+ /** @typedef {{ readonly kind: 'eof'}} EofState */
169
+
170
+ /** @typedef {number|null} CharCodeOrEof */
171
+
172
+ /** @typedef {(input: number) => readonly[list.List<FjsonToken>, TokenizerState]} ToToken */
173
+
174
+ /**
175
+ * @template T
176
+ * @typedef {(state: T) => ToToken} CreateToToken<T>
177
+ */
178
+
179
+ /** @typedef {list.List<_range.Range>} RangeSet */
180
+
181
+ /**
182
+ * @template T
183
+ * @typedef {(def: CreateToToken<T>) => (RangeMapToToken<T>)} RangeFunc<T>
184
+ */
185
+
186
+ /**
187
+ * @template T
188
+ * @typedef {range_map.RangeMapArray<CreateToToken<T>>} RangeMapToToken<T>
189
+ */
190
+
191
+ /** @type {(old: string) => (input: number) => string} */
192
+ const appendChar = old => input => `${old}${fromCharCode(input)}`
193
+
194
+ /** @type {<T>(def: CreateToToken<T>) => (a: CreateToToken<T>) => (b: CreateToToken<T>) => CreateToToken<T>} */
195
+ const union = def => a => b => {
196
+ if (a === def || a === b) { return b }
197
+ if (b === def) { return a }
198
+ throw [a, b]
199
+ }
200
+
201
+ /** @type {<T>(def: CreateToToken<T>) => range_map.RangeMerge<CreateToToken<T>>} */
202
+ const rangeMapMerge = def => merge({
203
+ union: union(def),
204
+ equal: operator.strictEqual,
205
+ })
206
+
207
+ /** @type {<T>(r: _range.Range) => (f: CreateToToken<T>) => RangeFunc<T>} */
208
+ const rangeFunc = r => f => def => fromRange(def)(r)(f)
209
+
210
+ /** @type {<T>(def: CreateToToken<T>) => (operator.Scan<RangeFunc<T>, RangeMapToToken<T>>)} */
211
+ const scanRangeOp = def => f => [f(def), scanRangeOp(def)]
212
+
213
+ /** @type {<T>(def: CreateToToken<T>) => (a: list.List<RangeFunc<T>>) => RangeMapToToken<T>} */
214
+ const reduceRangeMap = def => a => {
215
+ const rm = scan(scanRangeOp(def))(a)
216
+ return toArray(listReduce(rangeMapMerge(def))(empty)(rm))
217
+ }
218
+
219
+ /** @type {<T>(def: CreateToToken<T>) => (f: CreateToToken<T>) => (operator.Scan<_range.Range, RangeMapToToken<T>>)} */
220
+ const scanRangeSetOp = def => f => r => [fromRange(def)(r)(f), scanRangeSetOp(def)(f)]
221
+
222
+ /** @type {<T>(rs: list.List<_range.Range>) => (f: CreateToToken<T>) => RangeFunc<T>} */
223
+ const rangeSetFunc = rs => f => def => {
224
+ const rm = scan(scanRangeSetOp(def)(f))(rs)
225
+ return toArray(listReduce(rangeMapMerge(def))(empty)(rm))
226
+ }
227
+
228
+ /** @type {<T>(def: CreateToToken<T>) => (a: list.List<RangeFunc<T>>) => CreateToToken<T>} */
229
+ const create = def => a => {
230
+ /** @typedef {typeof def extends CreateToToken<infer T> ? T : never} T */
231
+ const i = reduceRangeMap(def)(a)
232
+ /** @type {(v: number) => (i: RangeMapToToken<T>) => (v: T) => ToToken} */
233
+ const x = get(def)
234
+ return v => c => x(c)(i)(v)(c)
235
+ }
236
+
237
+ /** @type {(digit: number) => bigint} */
238
+ const digitToBigInt = d => BigInt(d - digit0)
239
+
240
+ /** @type {(digit: number) => ParseNumberBuffer} */
241
+ const startNumber = digit => ({ s: 1n, m: digitToBigInt(digit), f: 0, es: 1, e: 0 })
242
+
243
+ /** @type {ParseNumberBuffer} */
244
+ const startNegativeNumber = { s: -1n, m: 0n, f: 0, es: 1, e: 0 }
245
+
246
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
247
+ const addIntDigit = digit => b => ({ ... b, m: b.m * 10n + digitToBigInt(digit)})
248
+
249
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
250
+ const addFracDigit = digit => b => ({ ... b, m: b.m * 10n + digitToBigInt(digit), f: b.f - 1})
251
+
252
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
253
+ const addExpDigit = digit => b => ({ ... b, e: b.e * 10 + digit - digit0})
254
+
255
+ /** @type {(s: ParseNumberState) => jsonTokenizer.NumberToken} */
256
+ const bufferToNumberToken = ({value, b}) => ({ kind: 'number', value: value, bf: [b.s * b.m, b.f + b.es * b.e] })
257
+
258
+ /** @type {(state: InitialState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
259
+ const initialStateOp = create(state => () => [[{ kind: 'error', message: 'unexpected character' }], state])([
260
+ rangeFunc(rangeOneNine)(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNumber(input), numberKind: 'int' }]),
261
+ rangeSetFunc(rangeIdStart)(() => input => [empty, { kind: 'id', value: fromCharCode(input) }]),
262
+ rangeSetFunc(rangeSetWhiteSpace)(state => () => [empty, state]),
263
+ rangeFunc(one(leftCurlyBracket))(state => () => [[{ kind: '{' }], state]),
264
+ rangeFunc(one(rightCurlyBracket))(state => () => [[{ kind: '}' }], state]),
265
+ rangeFunc(one(colon))(state => () => [[{ kind: ':' }], state]),
266
+ rangeFunc(one(comma))(state => () => [[{ kind: ',' }], state]),
267
+ rangeFunc(one(leftSquareBracket))(state => () => [[{ kind: '[' }], state]),
268
+ rangeFunc(one(rightSquareBracket))(state => () => [[{ kind: ']' }], state]),
269
+ rangeFunc(one(quotationMark))(() => () => [empty, { kind: 'string', value: '' }]),
270
+ rangeFunc(one(digit0))(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNumber(input), numberKind: '0' }]),
271
+ rangeFunc(one(hyphenMinus))(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNegativeNumber, numberKind: '-' }])
272
+ ])
273
+
274
+ /** @type {() => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
275
+ const invalidNumberToToken = () => input => tokenizeOp({ kind: 'invalidNumber' })(input)
276
+
277
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
278
+ const fullStopToToken = state => input => {
279
+ switch (state.numberKind) {
280
+ case '0':
281
+ case 'int': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: '.' }]
282
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
283
+ }
284
+ }
285
+
286
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
287
+ const digit0ToToken = state => input => {
288
+ switch (state.numberKind) {
289
+ case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
290
+ case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: '0' }]
291
+ case '.':
292
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addFracDigit(input)(state.b), numberKind: 'fractional' }]
293
+ case 'e':
294
+ case 'e+':
295
+ case 'e-':
296
+ case 'expDigits': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addExpDigit(input)(state.b), numberKind: 'expDigits' }]
297
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addIntDigit(input)(state.b), numberKind: state.numberKind }]
298
+ }
299
+ }
300
+
301
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
302
+ const digit19ToToken = state => input => {
303
+ switch (state.numberKind) {
304
+ case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
305
+ case '.':
306
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addFracDigit(input)(state.b), numberKind: 'fractional' }]
307
+ case 'e':
308
+ case 'e+':
309
+ case 'e-':
310
+ case 'expDigits': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addExpDigit(input)(state.b), numberKind: 'expDigits' }]
311
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addIntDigit(input)(state.b), numberKind: 'int' }]
312
+ }
313
+ }
314
+
315
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
316
+ const expToToken = state => input => {
317
+ switch (state.numberKind) {
318
+ case '0':
319
+ case 'int':
320
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: 'e' }]
321
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
322
+ }
323
+ }
324
+
325
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
326
+ const hyphenMinusToToken = state => input => {
327
+ switch (state.numberKind) {
328
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: { ... state.b, es: -1}, numberKind: 'e-' }]
329
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
330
+ }
331
+ }
332
+
333
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
334
+ const plusSignToToken = state => input => {
335
+ switch (state.numberKind) {
336
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: 'e+' }]
337
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
338
+ }
339
+ }
340
+
341
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
342
+ const terminalToToken = state => input => {
343
+ switch (state.numberKind) {
344
+ case '-':
345
+ case '.':
346
+ case 'e':
347
+ case 'e+':
348
+ case 'e-':
349
+ {
350
+ const next = tokenizeOp({ kind: 'initial' })(input)
351
+ return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
352
+ }
353
+ default:
354
+ {
355
+ const next = tokenizeOp({ kind: 'initial' })(input)
356
+ return [{ first: bufferToNumberToken(state), tail: next[0] }, next[1]]
357
+ }
358
+ }
359
+ }
360
+
361
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
362
+ const parseNumberStateOp = create(invalidNumberToToken)([
363
+ rangeFunc(one(fullStop))(fullStopToToken),
364
+ rangeFunc(one(digit0))(digit0ToToken),
365
+ rangeFunc(rangeOneNine)(digit19ToToken),
366
+ rangeSetFunc([one(latinSmallLetterE), one(latinCapitalLetterE)])(expToToken),
367
+ rangeFunc(one(hyphenMinus))(hyphenMinusToToken),
368
+ rangeFunc(one(plusSign))(plusSignToToken),
369
+ rangeSetFunc(rangeSetTerminalForNumber)(terminalToToken)
370
+ ])
371
+
372
+ /** @type {(state: InvalidNumberState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
373
+ const invalidNumberStateOp = create(() => () => [empty, { kind: 'invalidNumber' }])([
374
+ rangeSetFunc(rangeSetTerminalForNumber)(() => input => {
375
+ const next = tokenizeOp({ kind: 'initial' })(input)
376
+ return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
377
+ })
378
+ ])
379
+
380
+ /** @type {(state: ParseStringState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
381
+ const parseStringStateOp = create(state => input => [empty, { kind: 'string', value: appendChar(state.value)(input) }])([
382
+ rangeFunc(one(quotationMark))(state => () => [[{ kind: 'string', value: state.value }], { kind: 'initial' }]),
383
+ rangeFunc(one(reverseSolidus))(state => () => [empty, { kind: 'escapeChar', value: state.value }])
384
+ ])
385
+
386
+ /** @type {(state: ParseEscapeCharState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
387
+ const parseEscapeDefault = state => input => {
388
+ const next = tokenizeOp({ kind: 'string', value: state.value })(input)
389
+ return [{ first: { kind: 'error', message: 'unescaped character' }, tail: next[0] }, next[1]]
390
+ }
391
+
392
+ /** @type {(state: ParseEscapeCharState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
393
+ const parseEscapeCharStateOp = create(parseEscapeDefault)([
394
+ rangeSetFunc([one(quotationMark), one(reverseSolidus), one(solidus)])(state => input => [empty, { kind: 'string', value: appendChar(state.value)(input) }]),
395
+ rangeFunc(one(latinSmallLetterB))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(backspace) }]),
396
+ rangeFunc(one(latinSmallLetterF))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(ff) }]),
397
+ rangeFunc(one(latinSmallLetterN))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(lf) }]),
398
+ rangeFunc(one(latinSmallLetterR))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(cr) }]),
399
+ rangeFunc(one(latinSmallLetterT))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(ht) }]),
400
+ rangeFunc(one(latinSmallLetterU))(state => () => [empty, { kind: 'unicodeChar', value: state.value, unicode: 0, hexIndex: 0 }]),
401
+ ])
402
+
403
+ /** @type {(state: ParseUnicodeCharState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
404
+ const parseUnicodeCharDefault = state => input => {
405
+ const next = tokenizeOp({ kind: 'string', value: state.value })(input)
406
+ return [{ first: { kind: 'error', message: 'invalid hex value' }, tail: next[0] }, next[1]]
407
+ }
408
+
409
+ /** @type {(offser: number) => (state: ParseUnicodeCharState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
410
+ const parseUnicodeCharHex = offset => state => input => {
411
+ const hexValue = input - offset
412
+ const newUnicode = state.unicode | (hexValue << (3 - state.hexIndex) * 4)
413
+ return [empty, state.hexIndex === 3 ?
414
+ { kind: 'string', value: appendChar(state.value)(newUnicode) } :
415
+ { kind: 'unicodeChar', value: state.value, unicode: newUnicode, hexIndex: state.hexIndex + 1 }]
416
+ }
417
+
418
+ /** @type {(state: ParseUnicodeCharState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
419
+ const parseUnicodeCharStateOp = create(parseUnicodeCharDefault)([
420
+ rangeFunc(digitRange)(parseUnicodeCharHex(digit0)),
421
+ rangeFunc(rangeSmallAF)(parseUnicodeCharHex(latinSmallLetterA - 10)),
422
+ rangeFunc(rangeCapitalAF)(parseUnicodeCharHex(latinCapitalLetterA - 10))
423
+ ])
424
+
425
+ /** @type {(s: string) => FjsonToken} */
426
+ const idToToken = s => {
427
+ switch (s) {
428
+ case 'true': return { kind: 'true' }
429
+ case 'false': return { kind: 'false' }
430
+ case 'null': return { kind: 'null' }
431
+ default: return { kind: 'id', value: s }
432
+ }
433
+ }
434
+
435
+ /** @type {(state: ParseIdState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
436
+ const parseIdDefault = state => input => {
437
+ const keyWordToken = idToToken(state.value)
438
+ const next = tokenizeOp({ kind: 'initial' })(input)
439
+ return [{ first: keyWordToken, tail: next[0] }, next[1]]
440
+ }
441
+
442
+ /** @type {(state: ParseIdState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
443
+ const parseIdStateOp = create(parseIdDefault)([
444
+ rangeSetFunc(rangeId)(state => input => [empty, { kind: 'id', value: appendChar(state.value)(input) }])
445
+ ])
446
+
447
+ /** @type {(state: EofState) => (input: number) => readonly[list.List<FjsonToken>, TokenizerState]} */
448
+ const eofStateOp = create(state => () => [[{ kind: 'error', message: 'eof' }], state])([])
449
+
450
+ /** @type {operator.StateScan<number, TokenizerState, list.List<FjsonToken>>} */
451
+ const tokenizeCharCodeOp = state => {
452
+ switch (state.kind) {
453
+ case 'initial': return initialStateOp(state)
454
+ case 'id': return parseIdStateOp(state)
455
+ case 'string': return parseStringStateOp(state)
456
+ case 'escapeChar': return parseEscapeCharStateOp(state)
457
+ case 'unicodeChar': return parseUnicodeCharStateOp(state)
458
+ case 'invalidNumber': return invalidNumberStateOp(state)
459
+ case 'number': return parseNumberStateOp(state)
460
+ case 'eof': return eofStateOp(state)
461
+ }
462
+ }
463
+
464
+ /** @type {(state: TokenizerState) => readonly[list.List<FjsonToken>, TokenizerState]} */
465
+ const tokenizeEofOp = state => {
466
+ switch (state.kind) {
467
+ case 'initial': return [empty, { kind: 'eof' }]
468
+ case 'id': return [[idToToken(state.value)], { kind: 'eof' }]
469
+ case 'string':
470
+ case 'escapeChar':
471
+ case 'unicodeChar': return [[{ kind: 'error', message: '" are missing' }], { kind: 'eof' }]
472
+ case 'invalidNumber': return [[{ kind: 'error', message: 'invalid number' }], { kind: 'eof' }]
473
+ case 'number':
474
+ switch (state.numberKind) {
475
+ case '-':
476
+ case '.':
477
+ case 'e':
478
+ case 'e+':
479
+ case 'e-': return [[{ kind: 'error', message: 'invalid number' }], { kind: 'invalidNumber', }]
480
+ default: return [[bufferToNumberToken(state)], { kind: 'eof' }]
481
+ }
482
+ case 'eof': return [[{ kind: 'error', message: 'eof' }], state]
483
+ }
484
+ }
485
+
486
+ /** @type {operator.StateScan<CharCodeOrEof, TokenizerState, list.List<FjsonToken>>} */
487
+ const tokenizeOp = state => input => input === null ? tokenizeEofOp(state) : tokenizeCharCodeOp(state)(input)
488
+
489
+ const scanTokenize = stateScan(tokenizeOp)
490
+
491
+ const initial = scanTokenize({ kind: 'initial' })
492
+
493
+ /** @type {(input: list.List<number>) => list.List<FjsonToken>} */
494
+ const tokenize = input => flat(initial(flat([/** @type {list.List<CharCodeOrEof>} */(input), [null]])))
495
+
496
+ module.exports = {
497
+ /** @readonly */
498
+ tokenize
499
+ }
@@ -0,0 +1,279 @@
1
+ const tokenizer = require('./module.f.cjs')
2
+ const { toArray, countdown } = require('../../types/list/module.f.cjs')
3
+ const fjson = require('../../fjson/module.f.cjs')
4
+ const { sort } = require('../../types/object/module.f.cjs')
5
+ const encoding = require('../../text/utf16/module.f.cjs');
6
+
7
+ /** @type {(s: string) => readonly tokenizer.FjsonToken[]} */
8
+ const tokenizeString = s => toArray(tokenizer.tokenize(encoding.stringToList(s)))
9
+
10
+ const stringify = fjson.stringify(sort)
11
+
12
+ module.exports = {
13
+ testing: [
14
+ () => {
15
+ const result = tokenizeString('')
16
+ if (result.length !== 0) { throw result }
17
+ },
18
+ () => {
19
+ const result = stringify(tokenizeString('{'))
20
+ if (result !== '[{"kind":"{"}]') { throw result }
21
+ },
22
+ () => {
23
+ const result = stringify(tokenizeString('}'))
24
+ if (result !== '[{"kind":"}"}]') { throw result }
25
+ },
26
+ () => {
27
+ const result = stringify(tokenizeString(':'))
28
+ if (result !== '[{"kind":":"}]') { throw result }
29
+ },
30
+ () => {
31
+ const result = stringify(tokenizeString(','))
32
+ if (result !== '[{"kind":","}]') { throw result }
33
+ },
34
+ () => {
35
+ const result = stringify(tokenizeString('['))
36
+ if (result !== '[{"kind":"["}]') { throw result }
37
+ },
38
+ () => {
39
+ const result = stringify(tokenizeString(']'))
40
+ if (result !== '[{"kind":"]"}]') { throw result }
41
+ },
42
+ () => {
43
+ const result = stringify(tokenizeString('ᄑ'))
44
+ if (result !== '[{"kind":"error","message":"unexpected character"}]') { throw result }
45
+ },
46
+ () => {
47
+ const result = stringify(tokenizeString('err'))
48
+ if (result !== '[{"kind":"id","value":"err"}]') { throw result }
49
+ },
50
+ () => {
51
+ const result = stringify(tokenizeString('{e}'))
52
+ if (result !== '[{"kind":"{"},{"kind":"id","value":"e"},{"kind":"}"}]') { throw result }
53
+ },
54
+ () => {
55
+ const result = stringify(tokenizeString('{ \t\n\r}'))
56
+ if (result !== '[{"kind":"{"},{"kind":"}"}]') { throw result }
57
+ },
58
+ () => {
59
+ const result = stringify(tokenizeString('true'))
60
+ if (result !== '[{"kind":"true"}]') { throw result }
61
+ },
62
+ () => {
63
+ const result = stringify(tokenizeString('tru'))
64
+ if (result !== '[{"kind":"id","value":"tru"}]') { throw result }
65
+ },
66
+ () => {
67
+ const result = stringify(tokenizeString('false'))
68
+ if (result !== '[{"kind":"false"}]') { throw result }
69
+ },
70
+ () => {
71
+ const result = stringify(tokenizeString('null'))
72
+ if (result !== '[{"kind":"null"}]') { throw result }
73
+ },
74
+ () => {
75
+ const result = stringify(tokenizeString('[null]'))
76
+ if (result !== '[{"kind":"["},{"kind":"null"},{"kind":"]"}]') { throw result }
77
+ },
78
+ () => {
79
+ const result = stringify(tokenizeString('""'))
80
+ if (result !== '[{"kind":"string","value":""}]') { throw result }
81
+ },
82
+ () => {
83
+ const result = stringify(tokenizeString('"value"'))
84
+ if (result !== '[{"kind":"string","value":"value"}]') { throw result }
85
+ },
86
+ () => {
87
+ const result = stringify(tokenizeString('"value'))
88
+ if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
89
+ },
90
+ () => {
91
+ const result = stringify(tokenizeString('"value1" "value2"'))
92
+ if (result !== '[{"kind":"string","value":"value1"},{"kind":"string","value":"value2"}]') { throw result }
93
+ },
94
+ () => {
95
+ const result = stringify(tokenizeString('"'))
96
+ if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
97
+ },
98
+ () => {
99
+ const result = stringify(tokenizeString('"\\\\"'))
100
+ if (result !== '[{"kind":"string","value":"\\\\"}]') { throw result }
101
+ },
102
+ () => {
103
+ const result = stringify(tokenizeString('"\\""'))
104
+ if (result !== '[{"kind":"string","value":"\\""}]') { throw result }
105
+ },
106
+ () => {
107
+ const result = stringify(tokenizeString('"\\/"'))
108
+ if (result !== '[{"kind":"string","value":"/"}]') { throw result }
109
+ },
110
+ () => {
111
+ const result = stringify(tokenizeString('"\\x"'))
112
+ if (result !== '[{"kind":"error","message":"unescaped character"},{"kind":"string","value":"x"}]') { throw result }
113
+ },
114
+ () => {
115
+ const result = stringify(tokenizeString('"\\'))
116
+ if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
117
+ },
118
+ () => {
119
+ const result = stringify(tokenizeString('"\\b\\f\\n\\r\\t"'))
120
+ if (result !== '[{"kind":"string","value":"\\b\\f\\n\\r\\t"}]') { throw result }
121
+ },
122
+ () => {
123
+ const result = stringify(tokenizeString('"\\u1234"'))
124
+ if (result !== '[{"kind":"string","value":"ሴ"}]') { throw result }
125
+ },
126
+ () => {
127
+ const result = stringify(tokenizeString('"\\uaBcDEeFf"'))
128
+ if (result !== '[{"kind":"string","value":"ꯍEeFf"}]') { throw result }
129
+ },
130
+ () => {
131
+ const result = stringify(tokenizeString('"\\uEeFg"'))
132
+ if (result !== '[{"kind":"error","message":"invalid hex value"},{"kind":"string","value":"g"}]') { throw result }
133
+ },
134
+ () => {
135
+ const result = stringify(tokenizeString('0'))
136
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"0"}]') { throw result }
137
+ },
138
+ () => {
139
+ const result = stringify(tokenizeString('[0]'))
140
+ if (result !== '[{"kind":"["},{"bf":[0n,0],"kind":"number","value":"0"},{"kind":"]"}]') { throw result }
141
+ },
142
+ () => {
143
+ const result = stringify(tokenizeString('00'))
144
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
145
+ },
146
+ () => {
147
+ const result = stringify(tokenizeString('0abc,'))
148
+ if (result !== '[{"kind":"error","message":"invalid number"},{"kind":","}]') { throw result }
149
+ },
150
+ () => {
151
+ const result = stringify(tokenizeString('123456789012345678901234567890'))
152
+ if (result !== '[{"bf":[123456789012345678901234567890n,0],"kind":"number","value":"123456789012345678901234567890"}]') { throw result }
153
+ },
154
+ () => {
155
+ const result = stringify(tokenizeString('{90}'))
156
+ if (result !== '[{"kind":"{"},{"bf":[90n,0],"kind":"number","value":"90"},{"kind":"}"}]') { throw result }
157
+ },
158
+ () => {
159
+ const result = stringify(tokenizeString('1 2'))
160
+ if (result !== '[{"bf":[1n,0],"kind":"number","value":"1"},{"bf":[2n,0],"kind":"number","value":"2"}]') { throw result }
161
+ },
162
+ () => {
163
+ const result = stringify(tokenizeString('0. 2'))
164
+ if (result !== '[{"kind":"error","message":"invalid number"},{"bf":[2n,0],"kind":"number","value":"2"}]') { throw result }
165
+ },
166
+ () => {
167
+ const result = stringify(tokenizeString('10-0'))
168
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
169
+ },
170
+ () => {
171
+ const result = stringify(tokenizeString('9a:'))
172
+ if (result !== '[{"kind":"error","message":"invalid number"},{"kind":":"}]') { throw result }
173
+ },
174
+ () => {
175
+ const result = stringify(tokenizeString('-10'))
176
+ if (result !== '[{"bf":[-10n,0],"kind":"number","value":"-10"}]') { throw result }
177
+ },
178
+ () => {
179
+ const result = stringify(tokenizeString('-0'))
180
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"-0"}]') { throw result }
181
+ },
182
+ () => {
183
+ const result = stringify(tokenizeString('-00'))
184
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
185
+ },
186
+ () => {
187
+ const result = stringify(tokenizeString('-.123'))
188
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
189
+ },
190
+ () => {
191
+ const result = stringify(tokenizeString('0.01'))
192
+ if (result !== '[{"bf":[1n,-2],"kind":"number","value":"0.01"}]') { throw result }
193
+ },
194
+ () => {
195
+ const result = stringify(tokenizeString('-0.9'))
196
+ if (result !== '[{"bf":[-9n,-1],"kind":"number","value":"-0.9"}]') { throw result }
197
+ },
198
+ () => {
199
+ const result = stringify(tokenizeString('-0.'))
200
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
201
+ },
202
+ () => {
203
+ const result = stringify(tokenizeString('-0.]'))
204
+ if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"]"}]') { throw result }
205
+ },
206
+ () => {
207
+ const result = stringify(tokenizeString('12.34'))
208
+ if (result !== '[{"bf":[1234n,-2],"kind":"number","value":"12.34"}]') { throw result }
209
+ },
210
+ () => {
211
+ const result = stringify(tokenizeString('-12.00'))
212
+ if (result !== '[{"bf":[-1200n,-2],"kind":"number","value":"-12.00"}]') { throw result }
213
+ },
214
+ () => {
215
+ const result = stringify(tokenizeString('-12.'))
216
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
217
+ },
218
+ () => {
219
+ const result = stringify(tokenizeString('12.]'))
220
+ if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"]"}]') { throw result }
221
+ },
222
+ () => {
223
+ const result = stringify(tokenizeString('0e1'))
224
+ if (result !== '[{"bf":[0n,1],"kind":"number","value":"0e1"}]') { throw result }
225
+ },
226
+ () => {
227
+ const result = stringify(tokenizeString('0e+2'))
228
+ if (result !== '[{"bf":[0n,2],"kind":"number","value":"0e+2"}]') { throw result }
229
+ },
230
+ () => {
231
+ const result = stringify(tokenizeString('0e-0'))
232
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"0e-0"}]') { throw result }
233
+ },
234
+ () => {
235
+ const result = stringify(tokenizeString('12e0000'))
236
+ if (result !== '[{"bf":[12n,0],"kind":"number","value":"12e0000"}]') { throw result }
237
+ },
238
+ () => {
239
+ const result = stringify(tokenizeString('-12e-0001'))
240
+ if (result !== '[{"bf":[-12n,-1],"kind":"number","value":"-12e-0001"}]') { throw result }
241
+ },
242
+ () => {
243
+ const result = stringify(tokenizeString('-12.34e1234'))
244
+ if (result !== '[{"bf":[-1234n,1232],"kind":"number","value":"-12.34e1234"}]') { throw result }
245
+ },
246
+ () => {
247
+ const result = stringify(tokenizeString('0e'))
248
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
249
+ },
250
+ () => {
251
+ const result = stringify(tokenizeString('0e-'))
252
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
253
+ },
254
+ () => {
255
+ const result = stringify(tokenizeString('ABCdef1234567890$_'))
256
+ if (result !== '[{"kind":"id","value":"ABCdef1234567890$_"}]') { throw result }
257
+ },
258
+ () => {
259
+ const result = stringify(tokenizeString('{ABCdef1234567890$_}'))
260
+ if (result !== '[{"kind":"{"},{"kind":"id","value":"ABCdef1234567890$_"},{"kind":"}"}]') { throw result }
261
+ },
262
+ () => {
263
+ const result = stringify(tokenizeString('123 _123'))
264
+ if (result !== '[{"bf":[123n,0],"kind":"number","value":"123"},{"kind":"id","value":"_123"}]') { throw result }
265
+ },
266
+ () => {
267
+ const result = stringify(tokenizeString('123 $123'))
268
+ if (result !== '[{"bf":[123n,0],"kind":"number","value":"123"},{"kind":"id","value":"$123"}]') { throw result }
269
+ },
270
+ () => {
271
+ const result = stringify(tokenizeString('123_123'))
272
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
273
+ },
274
+ () => {
275
+ const result = stringify(tokenizeString('123$123'))
276
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
277
+ },
278
+ ]
279
+ }
package/fsc/README.md CHANGED
@@ -32,6 +32,7 @@
32
32
  - `...` - spread
33
33
  - `/` - division
34
34
  - `/*` - commentBegin
35
+ - `//` - oneLineComment
35
36
  - `/=` - divisionAssignment
36
37
  - `0..9` - numberBegin
37
38
  - `:` - colon
@@ -251,11 +251,5 @@ module.exports = {
251
251
  const result = stringify(tokenizeString('0e-'))
252
252
  if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
253
253
  },
254
- ],
255
- stress: () => {
256
- const n = 10_000
257
- const list = countdown(n)
258
- const result = tokenizer.tokenize(list)
259
- //if (toArray(result).length !== 9929) { throw result }
260
- }
254
+ ]
261
255
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.520",
3
+ "version": "0.0.522",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "module.f.cjs",
6
6
  "scripts": {