functionalscript 0.0.495 → 0.0.497

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.
@@ -1,7 +1,10 @@
1
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
2
4
  const list = require('../../types/list/module.f.cjs')
3
- const { contains } = require('../../types/range/module.f.cjs')
4
- const { empty, stateScan, flat } = list
5
+ const _range = require('../../types/range/module.f.cjs')
6
+ const { one } = _range
7
+ const { empty, stateScan, flat, toArray, reduce: listReduce, scan } = list
5
8
  const { fromCharCode } = String
6
9
  const {
7
10
  range,
@@ -72,11 +75,31 @@ const {
72
75
  * } JsonToken
73
76
  */
74
77
 
75
- const containsDigit = contains(digitRange)
76
- const containsDigitOneNine = contains(range('19'))
77
- const containsSmallAF = contains(range('af'))
78
- const containsCapitalAF = contains(range('AF'))
79
- const containsSmallLetter = contains(latinSmallLetterRange)
78
+ const rangeOneNine = range('19')
79
+
80
+ const rangeSetWhiteSpace = [
81
+ one(ht),
82
+ one(lf),
83
+ one(cr),
84
+ one(space)
85
+ ]
86
+
87
+ const rangeSetTerminalForNumer = [
88
+ one(ht),
89
+ one(lf),
90
+ one(cr),
91
+ one(space),
92
+ one(quotationMark),
93
+ one(comma),
94
+ one(leftCurlyBracket),
95
+ one(rightCurlyBracket),
96
+ one(leftSquareBracket),
97
+ one(rightSquareBracket),
98
+ one(colon)
99
+ ]
100
+
101
+ const rangeSmallAF = range('af')
102
+ const rangeCapitalAF = range('AF')
80
103
 
81
104
  /**
82
105
  * @typedef {|
@@ -127,192 +150,235 @@ const containsSmallLetter = contains(latinSmallLetterRange)
127
150
 
128
151
  /** @typedef {number|null} CharCodeOrEof */
129
152
 
153
+ /** @typedef {(input: number) => readonly[list.List<JsonToken>, TokenizerState]} ToToken */
154
+
155
+ /**
156
+ * @template T
157
+ * @typedef {(state: T) => ToToken} CreateToToken<T>
158
+ */
159
+
160
+ /** @typedef {list.List<_range.Range>} RangeSet */
161
+
162
+ /**
163
+ * @template T
164
+ * @typedef {(def: CreateToToken<T>) => (RangeMapToToken<T>)} RangeFunc<T>
165
+ */
166
+
167
+ /**
168
+ * @template T
169
+ * @typedef {range_map.RangeMapArray<CreateToToken<T>>} RangeMapToToken<T>
170
+ */
171
+
130
172
  /** @type {(old: string) => (input: number) => string} */
131
173
  const appendChar = old => input => `${old}${fromCharCode(input)}`
132
174
 
175
+ /** @type {<T>(def: CreateToToken<T>) => (a: CreateToToken<T>) => (b: CreateToToken<T>) => CreateToToken<T>} */
176
+ const union = def => a => b => {
177
+ if (a === def || a === b) { return b }
178
+ if (b === def) { return a }
179
+ throw [a, b]
180
+ }
181
+
182
+ /** @type {<T>(def: CreateToToken<T>) => range_map.RangeMerge<CreateToToken<T>>} */
183
+ const rangeMapMerge = def => merge({
184
+ union: union(def),
185
+ equal: operator.strictEqual,
186
+ })
187
+
188
+ /** @type {<T>(r: _range.Range) => (f: CreateToToken<T>) => RangeFunc<T>} */
189
+ const rangeFunc = r => f => def => fromRange(def)(r)(f)
190
+
191
+ /** @type {<T>(def: CreateToToken<T>) => (operator.Scan<RangeFunc<T>, RangeMapToToken<T>>)} */
192
+ const scanRangeOp = def => f => [f(def), scanRangeOp(def)]
193
+
194
+ /** @type {<T>(def: CreateToToken<T>) => (a: list.List<RangeFunc<T>>) => RangeMapToToken<T>} */
195
+ const reduceRangeMap = def => a => {
196
+ const rm = scan(scanRangeOp(def))(a)
197
+ return toArray(listReduce(rangeMapMerge(def))(empty)(rm))
198
+ }
199
+
200
+ /** @type {<T>(def: CreateToToken<T>) => (f: CreateToToken<T>) => (operator.Scan<_range.Range, RangeMapToToken<T>>)} */
201
+ const scanRangeSetOp = def => f => r => [fromRange(def)(r)(f), scanRangeSetOp(def)(f)]
202
+
203
+ /** @type {<T>(rs: list.List<_range.Range>) => (f: CreateToToken<T>) => RangeFunc<T>} */
204
+ const rangeSetFunc = rs => f => def => {
205
+ const rm = scan(scanRangeSetOp(def)(f))(rs)
206
+ return toArray(listReduce(rangeMapMerge(def))(empty)(rm))
207
+ }
208
+
209
+ /** @type {<T>(def: CreateToToken<T>) => (a: list.List<RangeFunc<T>>) => CreateToToken<T>} */
210
+ const create = def => a => {
211
+ /** @typedef {typeof def extends CreateToToken<infer T> ? T : never} T */
212
+ const i = reduceRangeMap(def)(a)
213
+ /** @type {(v: number) => (i: RangeMapToToken<T>) => (v: T) => ToToken} */
214
+ const x = get(def)
215
+ return v => c => x(c)(i)(v)(c)
216
+ }
217
+
133
218
  /** @type {(state: InitialState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
134
- const initialStateOp = initialState => input => {
135
- if (containsDigitOneNine(input)) {
136
- return [empty, { kind: 'number', value: fromCharCode(input), numberKind: 'int' }]
137
- }
138
- if (containsSmallLetter(input)) {
139
- return [empty, { kind: 'keyword', value: fromCharCode(input) }]
140
- }
141
- if (isWhiteSpace(input)) {
142
- return [empty, initialState]
143
- }
144
- switch (input) {
145
- case leftCurlyBracket: return [[{ kind: '{' }], initialState]
146
- case rightCurlyBracket: return [[{ kind: '}' }], initialState]
147
- case colon: return [[{ kind: ':' }], initialState]
148
- case comma: return [[{ kind: ',' }], initialState]
149
- case leftSquareBracket: return [[{ kind: '[' }], initialState]
150
- case rightSquareBracket: return [[{ kind: ']' }], initialState]
151
- case quotationMark: return [empty, { kind: 'string', value: '' }]
152
- case digit0: return [empty, { kind: 'number', value: fromCharCode(input), numberKind: '0' }]
153
- case hyphenMinus: return [empty, { kind: 'number', value: fromCharCode(input), numberKind: '-' }]
154
- default: return [[{ kind: 'error', message: 'unexpected character' }], initialState]
219
+ const initialStateOp = create(state => () => [[{ kind: 'error', message: 'unexpected character' }], state])([
220
+ rangeFunc(rangeOneNine)(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: 'int' }]),
221
+ rangeFunc(latinSmallLetterRange)(() => input => [empty, { kind: 'keyword', value: fromCharCode(input) }]),
222
+ rangeSetFunc(rangeSetWhiteSpace)(state => () => [empty, state]),
223
+ rangeFunc(one(leftCurlyBracket))(state => () => [[{ kind: '{' }], state]),
224
+ rangeFunc(one(rightCurlyBracket))(state => () => [[{ kind: '}' }], state]),
225
+ rangeFunc(one(colon))(state => () => [[{ kind: ':' }], state]),
226
+ rangeFunc(one(comma))(state => () => [[{ kind: ',' }], state]),
227
+ rangeFunc(one(leftSquareBracket))(state => () => [[{ kind: '[' }], state]),
228
+ rangeFunc(one(rightSquareBracket))(state => () => [[{ kind: ']' }], state]),
229
+ rangeFunc(one(quotationMark))(() => () => [empty, { kind: 'string', value: '' }]),
230
+ rangeFunc(one(digit0))(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: '0' }]),
231
+ rangeFunc(one(hyphenMinus))(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: '-' }])
232
+ ])
233
+
234
+ /** @type {(state: any) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
235
+ const invalidNumberToToken = () => input => tokenizeOp({ kind: 'invalidNumber' })(input)
236
+
237
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
238
+ const fullStopToToken = state => input => {
239
+ switch (state.numberKind) {
240
+ case '0':
241
+ case 'int': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '.' }]
242
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
155
243
  }
156
244
  }
157
245
 
158
246
  /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
159
- const parseNumberStateOp = state => input => {
160
- if (input === fullStop) {
161
- switch (state.numberKind) {
162
- case '0':
163
- case 'int': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '.' }]
164
- default: return tokenizeOp({ kind: 'invalidNumber' })(input)
165
- }
166
- }
167
- if (input === digit0) {
168
- switch (state.numberKind) {
169
- case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
170
- case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '0' }]
171
- case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
172
- case 'e':
173
- case 'e+':
174
- case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
175
- default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
176
- }
177
- }
178
- if (containsDigitOneNine(input)) {
179
- switch (state.numberKind) {
180
- case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
181
- case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'int' }]
182
- case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
183
- case 'e':
184
- case 'e+':
185
- case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
186
- default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
187
- }
188
- }
189
- if (input === latinSmallLetterE || input === latinCapitalLetterE) {
190
- switch (state.numberKind) {
191
- case '0':
192
- case 'int':
193
- case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e' }]
194
- default: return tokenizeOp({ kind: 'invalidNumber' })(input)
195
- }
247
+ const digit0ToToken = state => input => {
248
+ switch (state.numberKind) {
249
+ case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
250
+ case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '0' }]
251
+ case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
252
+ case 'e':
253
+ case 'e+':
254
+ case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
255
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
196
256
  }
197
- if (input === hyphenMinus) {
198
- switch (state.numberKind) {
199
- case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e-' }]
200
- default: return tokenizeOp({ kind: 'invalidNumber' })(input)
201
- }
202
- }
203
- if (input === plusSign) {
204
- switch (state.numberKind) {
205
- case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e+' }]
206
- default: return tokenizeOp({ kind: 'invalidNumber' })(input)
207
- }
257
+ }
258
+
259
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
260
+ const digit19ToToken = state => input => {
261
+ switch (state.numberKind) {
262
+ case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
263
+ case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'int' }]
264
+ case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
265
+ case 'e':
266
+ case 'e+':
267
+ case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
268
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
208
269
  }
209
- if (isTerminalForNumber(input)) {
210
- switch (state.numberKind) {
211
- case '-':
212
- case '.':
213
- case 'e':
214
- case 'e+':
215
- case 'e-':
216
- {
217
- const next = tokenizeOp({ kind: 'initial' })(input)
218
- return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
219
- }
220
- default:
221
- {
222
- const next = tokenizeOp({ kind: 'initial' })(input)
223
- return [{ first: { kind: 'number', value: state.value }, tail: next[0] }, next[1]]
224
- }
225
- }
270
+ }
271
+
272
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
273
+ const expToToken = state => input => {
274
+ switch (state.numberKind) {
275
+ case '0':
276
+ case 'int':
277
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e' }]
278
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
226
279
  }
227
- return tokenizeOp({ kind: 'invalidNumber' })(input)
228
280
  }
229
281
 
230
- /** @type {(char: number) => boolean} */
231
- const isTerminalForNumber = char => {
232
- if (isWhiteSpace(char)) {
233
- return true;
282
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
283
+ const hyphenMinusToToken = state => input => {
284
+ switch (state.numberKind) {
285
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e-' }]
286
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
234
287
  }
235
- switch (char) {
236
- case quotationMark:
237
- case comma:
238
- case leftCurlyBracket:
239
- case rightCurlyBracket:
240
- case leftSquareBracket:
241
- case rightSquareBracket:
242
- case colon: return true
243
- default: return false
288
+ }
289
+
290
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
291
+ const plusSignToToken = state => input => {
292
+ switch (state.numberKind) {
293
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e+' }]
294
+ default: return tokenizeOp({ kind: 'invalidNumber' })(input)
244
295
  }
245
296
  }
246
297
 
247
- /** @type {(char: number) => boolean} */
248
- const isWhiteSpace = char => {
249
- switch (char) {
250
- case ht:
251
- case lf:
252
- case cr:
253
- case space: return true
254
- default: return false
298
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
299
+ const terminalToToken = state => input => {
300
+ switch (state.numberKind) {
301
+ case '-':
302
+ case '.':
303
+ case 'e':
304
+ case 'e+':
305
+ case 'e-':
306
+ {
307
+ const next = tokenizeOp({ kind: 'initial' })(input)
308
+ return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
309
+ }
310
+ default:
311
+ {
312
+ const next = tokenizeOp({ kind: 'initial' })(input)
313
+ return [{ first: { kind: 'number', value: state.value }, tail: next[0] }, next[1]]
314
+ }
255
315
  }
256
316
  }
257
317
 
318
+ /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
319
+ const parseNumberStateOp = create(invalidNumberToToken)([
320
+ rangeFunc(one(fullStop))(fullStopToToken),
321
+ rangeFunc(one(digit0))(digit0ToToken),
322
+ rangeFunc(rangeOneNine)(digit19ToToken),
323
+ rangeSetFunc([one(latinSmallLetterE), one(latinCapitalLetterE)])(expToToken),
324
+ rangeFunc(one(hyphenMinus))(hyphenMinusToToken),
325
+ rangeFunc(one(plusSign))(plusSignToToken),
326
+ rangeSetFunc(rangeSetTerminalForNumer)(terminalToToken)
327
+ ])
328
+
258
329
  /** @type {(state: InvalidNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
259
- const invalidNumberStateOp = () => input => {
260
- if (isTerminalForNumber(input)) {
330
+ const invalidNumberStateOp = create(() => () => [empty, { kind: 'invalidNumber' }])([
331
+ rangeSetFunc(rangeSetTerminalForNumer)(() => input => {
261
332
  const next = tokenizeOp({ kind: 'initial' })(input)
262
333
  return [{ first: { kind: 'error', message: 'invalid number' }, tail: next[0] }, next[1]]
263
- }
264
- return [empty, { kind: 'invalidNumber' }]
265
- }
334
+ })
335
+ ])
266
336
 
267
337
  /** @type {(state: ParseStringState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
268
- const parseStringStateOp = state => input => {
269
- switch (input) {
270
- case quotationMark: return [[{ kind: 'string', value: state.value }], { kind: 'initial' }]
271
- case reverseSolidus: return [empty, { kind: 'escapeChar', value: state.value }]
272
- default: return [empty, { kind: 'string', value: appendChar(state.value)(input) }]
273
- }
274
- }
338
+ const parseStringStateOp = create(state => input => [empty, { kind: 'string', value: appendChar(state.value)(input) }])([
339
+ rangeFunc(one(quotationMark))(state => () => [[{ kind: 'string', value: state.value }], { kind: 'initial' }]),
340
+ rangeFunc(one(reverseSolidus))(state => () => [empty, { kind: 'escapeChar', value: state.value }])
341
+ ])
275
342
 
276
343
  /** @type {(state: ParseEscapeCharState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
277
- const parseEscapeCharStateOp = state => input => {
278
- switch (input) {
279
- case quotationMark:
280
- case reverseSolidus:
281
- case solidus: return [empty, { kind: 'string', value: appendChar(state.value)(input) }]
282
- case latinSmallLetterB: return [empty, { kind: 'string', value: appendChar(state.value)(backspace) }]
283
- case latinSmallLetterF: return [empty, { kind: 'string', value: appendChar(state.value)(ff) }]
284
- case latinSmallLetterN: return [empty, { kind: 'string', value: appendChar(state.value)(lf) }]
285
- case latinSmallLetterR: return [empty, { kind: 'string', value: appendChar(state.value)(cr) }]
286
- case latinSmallLetterT: return [empty, { kind: 'string', value: appendChar(state.value)(ht) }]
287
- case latinSmallLetterU: return [empty, { kind: 'unicodeChar', value: state.value, unicode: 0, hexIndex: 0 }]
288
- default: {
289
- const next = tokenizeOp({ kind: 'string', value: state.value })(input)
290
- return [{ first: { kind: 'error', message: 'unescaped character' }, tail: next[0] }, next[1]]
291
- }
292
- }
344
+ const parseEscapeDefault = state => input => {
345
+ const next = tokenizeOp({ kind: 'string', value: state.value })(input)
346
+ return [{ first: { kind: 'error', message: 'unescaped character' }, tail: next[0] }, next[1]]
293
347
  }
294
348
 
295
- /** @type {(hex: number) => number|null} */
296
- const hexDigitToNumber = hex => {
297
- if (containsDigit(hex)) { return hex - digit0 }
298
- if (containsCapitalAF(hex)) { return hex - latinCapitalLetterA + 10 }
299
- if (containsSmallAF(hex)) { return hex - latinSmallLetterA + 10 }
300
- return null
301
- }
349
+ /** @type {(state: ParseEscapeCharState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
350
+ const parseEscapeCharStateOp = create(parseEscapeDefault)([
351
+ rangeSetFunc([one(quotationMark), one(reverseSolidus), one(solidus)])(state => input => [empty, { kind: 'string', value: appendChar(state.value)(input) }]),
352
+ rangeFunc(one(latinSmallLetterB))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(backspace) }]),
353
+ rangeFunc(one(latinSmallLetterF))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(ff) }]),
354
+ rangeFunc(one(latinSmallLetterN))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(lf) }]),
355
+ rangeFunc(one(latinSmallLetterR))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(cr) }]),
356
+ rangeFunc(one(latinSmallLetterT))(state => () => [empty, { kind: 'string', value: appendChar(state.value)(ht) }]),
357
+ rangeFunc(one(latinSmallLetterU))(state => () => [empty, { kind: 'unicodeChar', value: state.value, unicode: 0, hexIndex: 0 }]),
358
+ ])
302
359
 
303
360
  /** @type {(state: ParseUnicodeCharState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
304
- const parseUnicodeCharStateOp = state => input => {
305
- const hexValue = hexDigitToNumber(input)
306
- if (hexValue === null) {
307
- const next = tokenizeOp({ kind: 'string', value: state.value })(input)
308
- return [{ first: { kind: 'error', message: 'invalid hex value' }, tail: next[0] }, next[1]]
309
- }
361
+ const parseUnicodeCharDefault = state => input => {
362
+ const next = tokenizeOp({ kind: 'string', value: state.value })(input)
363
+ return [{ first: { kind: 'error', message: 'invalid hex value' }, tail: next[0] }, next[1]]
364
+ }
365
+
366
+ /** @type {(offser: number) => (state: ParseUnicodeCharState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
367
+ const parseUnicodeCharHex = offset => state => input => {
368
+ const hexValue = input - offset
310
369
  const newUnicode = state.unicode | (hexValue << (3 - state.hexIndex) * 4)
311
370
  return [empty, state.hexIndex === 3 ?
312
371
  { kind: 'string', value: appendChar(state.value)(newUnicode) } :
313
372
  { kind: 'unicodeChar', value: state.value, unicode: newUnicode, hexIndex: state.hexIndex + 1 }]
314
373
  }
315
374
 
375
+ /** @type {(state: ParseUnicodeCharState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
376
+ const parseUnicodeCharStateOp = create(parseUnicodeCharDefault)([
377
+ rangeFunc(digitRange)(parseUnicodeCharHex(digit0)),
378
+ rangeFunc(rangeSmallAF)(parseUnicodeCharHex(latinSmallLetterA - 10)),
379
+ rangeFunc(rangeCapitalAF)(parseUnicodeCharHex(latinCapitalLetterA - 10))
380
+ ])
381
+
316
382
  /** @type {(s: string) => JsonToken} */
317
383
  const stringToKeywordToken = s => {
318
384
  switch (s) {
@@ -324,17 +390,19 @@ const stringToKeywordToken = s => {
324
390
  }
325
391
 
326
392
  /** @type {(state: ParseKeywordState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
327
- const parseKeyWordStateOp = state => input => {
328
- if (containsSmallLetter(input)) {
329
- return [empty, { kind: 'keyword', value: appendChar(state.value)(input) }]
330
- }
393
+ const parseKeyWordDefault = state => input => {
331
394
  const keyWordToken = stringToKeywordToken(state.value)
332
395
  const next = tokenizeOp({ kind: 'initial' })(input)
333
396
  return [{ first: keyWordToken, tail: next[0] }, next[1]]
334
397
  }
335
398
 
399
+ /** @type {(state: ParseKeywordState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
400
+ const parseKeyWordStateOp = create(parseKeyWordDefault)([
401
+ rangeFunc(latinSmallLetterRange)(state => input => [empty, { kind: 'keyword', value: appendChar(state.value)(input) }])
402
+ ])
403
+
336
404
  /** @type {(state: EofState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
337
- const eofStateOp = state => () => [[{ kind: 'error', message: 'eof' }], state]
405
+ const eofStateOp = create(state => () => [[{ kind: 'error', message: 'eof' }], state])([])
338
406
 
339
407
  /** @type {operator.StateScan<number, TokenizerState, list.List<JsonToken>>} */
340
408
  const tokenizeCharCodeOp = state => {
@@ -1,249 +1,257 @@
1
1
  const tokenizer = require('./module.f.cjs')
2
- const list = require('../../types/list/module.f.cjs')
2
+ const { toArray, countdown } = require('../../types/list/module.f.cjs')
3
3
  const json = require('../module.f.cjs')
4
4
  const { sort } = require('../../types/object/module.f.cjs')
5
5
  const encoding = require('../../text/utf16/module.f.cjs');
6
6
 
7
7
  /** @type {(s: string) => readonly tokenizer.JsonToken[]} */
8
- const tokenizeString = s => list.toArray(tokenizer.tokenize(encoding.stringToList(s)))
8
+ const tokenizeString = s => toArray(tokenizer.tokenize(encoding.stringToList(s)))
9
9
 
10
10
  const stringify = json.stringify(sort)
11
11
 
12
- module.exports = [
13
- () => {
14
- const result = tokenizeString('')
15
- if (result.length !== 0) { throw result }
16
- },
17
- () => {
18
- const result = stringify(tokenizeString('{'))
19
- if (result !== '[{"kind":"{"}]') { throw result }
20
- },
21
- () => {
22
- const result = stringify(tokenizeString('}'))
23
- if (result !== '[{"kind":"}"}]') { throw result }
24
- },
25
- () => {
26
- const result = stringify(tokenizeString(':'))
27
- if (result !== '[{"kind":":"}]') { throw result }
28
- },
29
- () => {
30
- const result = stringify(tokenizeString(','))
31
- if (result !== '[{"kind":","}]') { throw result }
32
- },
33
- () => {
34
- const result = stringify(tokenizeString('['))
35
- if (result !== '[{"kind":"["}]') { throw result }
36
- },
37
- () => {
38
- const result = stringify(tokenizeString(']'))
39
- if (result !== '[{"kind":"]"}]') { throw result }
40
- },
41
- () => {
42
- const result = stringify(tokenizeString('ᄑ'))
43
- if (result !== '[{"kind":"error","message":"unexpected character"}]') { throw result }
44
- },
45
- () => {
46
- const result = stringify(tokenizeString('err'))
47
- if (result !== '[{"kind":"error","message":"invalid keyword"}]') { throw result }
48
- },
49
- () => {
50
- const result = stringify(tokenizeString('{e}'))
51
- if (result !== '[{"kind":"{"},{"kind":"error","message":"invalid keyword"},{"kind":"}"}]') { throw result }
52
- },
53
- () => {
54
- const result = stringify(tokenizeString('{ \t\n\r}'))
55
- if (result !== '[{"kind":"{"},{"kind":"}"}]') { throw result }
56
- },
57
- () => {
58
- const result = stringify(tokenizeString('true'))
59
- if (result !== '[{"kind":"true"}]') { throw result }
60
- },
61
- () => {
62
- const result = stringify(tokenizeString('tru'))
63
- if (result !== '[{"kind":"error","message":"invalid keyword"}]') { throw result }
64
- },
65
- () => {
66
- const result = stringify(tokenizeString('false'))
67
- if (result !== '[{"kind":"false"}]') { throw result }
68
- },
69
- () => {
70
- const result = stringify(tokenizeString('null'))
71
- if (result !== '[{"kind":"null"}]') { throw result }
72
- },
73
- () => {
74
- const result = stringify(tokenizeString('[null]'))
75
- if (result !== '[{"kind":"["},{"kind":"null"},{"kind":"]"}]') { throw result }
76
- },
77
- () => {
78
- const result = stringify(tokenizeString('""'))
79
- if (result !== '[{"kind":"string","value":""}]') { throw result }
80
- },
81
- () => {
82
- const result = stringify(tokenizeString('"value"'))
83
- if (result !== '[{"kind":"string","value":"value"}]') { throw result }
84
- },
85
- () => {
86
- const result = stringify(tokenizeString('"value'))
87
- if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
88
- },
89
- () => {
90
- const result = stringify(tokenizeString('"value1" "value2"'))
91
- if (result !== '[{"kind":"string","value":"value1"},{"kind":"string","value":"value2"}]') { throw result }
92
- },
93
- () => {
94
- const result = stringify(tokenizeString('"'))
95
- if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
96
- },
97
- () => {
98
- const result = stringify(tokenizeString('"\\\\"'))
99
- if (result !== '[{"kind":"string","value":"\\\\"}]') { throw result }
100
- },
101
- () => {
102
- const result = stringify(tokenizeString('"\\""'))
103
- if (result !== '[{"kind":"string","value":"\\""}]') { throw result }
104
- },
105
- () => {
106
- const result = stringify(tokenizeString('"\\/"'))
107
- if (result !== '[{"kind":"string","value":"/"}]') { throw result }
108
- },
109
- () => {
110
- const result = stringify(tokenizeString('"\\x"'))
111
- if (result !== '[{"kind":"error","message":"unescaped character"},{"kind":"string","value":"x"}]') { throw result }
112
- },
113
- () => {
114
- const result = stringify(tokenizeString('"\\'))
115
- if (result !== '[{"kind":"error","message":"\\" are missing"}]') { throw result }
116
- },
117
- () => {
118
- const result = stringify(tokenizeString('"\\b\\f\\n\\r\\t"'))
119
- if (result !== '[{"kind":"string","value":"\\b\\f\\n\\r\\t"}]') { throw result }
120
- },
121
- () => {
122
- const result = stringify(tokenizeString('"\\u1234"'))
123
- if (result !== '[{"kind":"string","value":"ሴ"}]') { throw result }
124
- },
125
- () => {
126
- const result = stringify(tokenizeString('"\\uaBcDEeFf"'))
127
- if (result !== '[{"kind":"string","value":"ꯍEeFf"}]') { throw result }
128
- },
129
- () => {
130
- const result = stringify(tokenizeString('"\\uEeFg"'))
131
- if (result !== '[{"kind":"error","message":"invalid hex value"},{"kind":"string","value":"g"}]') { throw result }
132
- },
133
- () => {
134
- const result = stringify(tokenizeString('0'))
135
- if (result !== '[{"kind":"number","value":"0"}]') { throw result }
136
- },
137
- () => {
138
- const result = stringify(tokenizeString('[0]'))
139
- if (result !== '[{"kind":"["},{"kind":"number","value":"0"},{"kind":"]"}]') { throw result }
140
- },
141
- () => {
142
- const result = stringify(tokenizeString('00'))
143
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
144
- },
145
- () => {
146
- const result = stringify(tokenizeString('0abc,'))
147
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":","}]') { throw result }
148
- },
149
- () => {
150
- const result = stringify(tokenizeString('1234567890'))
151
- if (result !== '[{"kind":"number","value":"1234567890"}]') { throw result }
152
- },
153
- () => {
154
- const result = stringify(tokenizeString('{90}'))
155
- if (result !== '[{"kind":"{"},{"kind":"number","value":"90"},{"kind":"}"}]') { throw result }
156
- },
157
- () => {
158
- const result = stringify(tokenizeString('1 2'))
159
- if (result !== '[{"kind":"number","value":"1"},{"kind":"number","value":"2"}]') { throw result }
160
- },
161
- () => {
162
- const result = stringify(tokenizeString('0. 2'))
163
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"number","value":"2"}]') { throw result }
164
- },
165
- () => {
166
- const result = stringify(tokenizeString('10-0'))
167
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
168
- },
169
- () => {
170
- const result = stringify(tokenizeString('9a:'))
171
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":":"}]') { throw result }
172
- },
173
- () => {
174
- const result = stringify(tokenizeString('-10'))
175
- if (result !== '[{"kind":"number","value":"-10"}]') { throw result }
176
- },
177
- () => {
178
- const result = stringify(tokenizeString('-0'))
179
- if (result !== '[{"kind":"number","value":"-0"}]') { throw result }
180
- },
181
- () => {
182
- const result = stringify(tokenizeString('-00'))
183
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
184
- },
185
- () => {
186
- const result = stringify(tokenizeString('-.123'))
187
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
188
- },
189
- () => {
190
- const result = stringify(tokenizeString('0.01'))
191
- if (result !== '[{"kind":"number","value":"0.01"}]') { throw result }
192
- },
193
- () => {
194
- const result = stringify(tokenizeString('-0.9'))
195
- if (result !== '[{"kind":"number","value":"-0.9"}]') { throw result }
196
- },
197
- () => {
198
- const result = stringify(tokenizeString('-0.'))
199
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
200
- },
201
- () => {
202
- const result = stringify(tokenizeString('-0.]'))
203
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"]"}]') { throw result }
204
- },
205
- () => {
206
- const result = stringify(tokenizeString('12.34'))
207
- if (result !== '[{"kind":"number","value":"12.34"}]') { throw result }
208
- },
209
- () => {
210
- const result = stringify(tokenizeString('-12.00'))
211
- if (result !== '[{"kind":"number","value":"-12.00"}]') { throw result }
212
- },
213
- () => {
214
- const result = stringify(tokenizeString('-12.'))
215
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
216
- },
217
- () => {
218
- const result = stringify(tokenizeString('12.]'))
219
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"]"}]') { throw result }
220
- },
221
- () => {
222
- const result = stringify(tokenizeString('0e1'))
223
- if (result !== '[{"kind":"number","value":"0e1"}]') { throw result }
224
- },
225
- () => {
226
- const result = stringify(tokenizeString('0e+2'))
227
- if (result !== '[{"kind":"number","value":"0e+2"}]') { throw result }
228
- },
229
- () => {
230
- const result = stringify(tokenizeString('0e-0'))
231
- if (result !== '[{"kind":"number","value":"0e-0"}]') { throw result }
232
- },
233
- () => {
234
- const result = stringify(tokenizeString('12e0000'))
235
- if (result !== '[{"kind":"number","value":"12e0000"}]') { throw result }
236
- },
237
- () => {
238
- const result = stringify(tokenizeString('-12e-0001'))
239
- if (result !== '[{"kind":"number","value":"-12e-0001"}]') { throw result }
240
- },
241
- () => {
242
- const result = stringify(tokenizeString('0e'))
243
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
244
- },
245
- () => {
246
- const result = stringify(tokenizeString('0e-'))
247
- if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
248
- },
249
- ]
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":"error","message":"invalid keyword"}]') { throw result }
49
+ },
50
+ () => {
51
+ const result = stringify(tokenizeString('{e}'))
52
+ if (result !== '[{"kind":"{"},{"kind":"error","message":"invalid keyword"},{"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":"error","message":"invalid keyword"}]') { 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 !== '[{"kind":"number","value":"0"}]') { throw result }
137
+ },
138
+ () => {
139
+ const result = stringify(tokenizeString('[0]'))
140
+ if (result !== '[{"kind":"["},{"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('1234567890'))
152
+ if (result !== '[{"kind":"number","value":"1234567890"}]') { throw result }
153
+ },
154
+ () => {
155
+ const result = stringify(tokenizeString('{90}'))
156
+ if (result !== '[{"kind":"{"},{"kind":"number","value":"90"},{"kind":"}"}]') { throw result }
157
+ },
158
+ () => {
159
+ const result = stringify(tokenizeString('1 2'))
160
+ if (result !== '[{"kind":"number","value":"1"},{"kind":"number","value":"2"}]') { throw result }
161
+ },
162
+ () => {
163
+ const result = stringify(tokenizeString('0. 2'))
164
+ if (result !== '[{"kind":"error","message":"invalid number"},{"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 !== '[{"kind":"number","value":"-10"}]') { throw result }
177
+ },
178
+ () => {
179
+ const result = stringify(tokenizeString('-0'))
180
+ if (result !== '[{"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 !== '[{"kind":"number","value":"0.01"}]') { throw result }
193
+ },
194
+ () => {
195
+ const result = stringify(tokenizeString('-0.9'))
196
+ if (result !== '[{"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 !== '[{"kind":"number","value":"12.34"}]') { throw result }
209
+ },
210
+ () => {
211
+ const result = stringify(tokenizeString('-12.00'))
212
+ if (result !== '[{"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 !== '[{"kind":"number","value":"0e1"}]') { throw result }
225
+ },
226
+ () => {
227
+ const result = stringify(tokenizeString('0e+2'))
228
+ if (result !== '[{"kind":"number","value":"0e+2"}]') { throw result }
229
+ },
230
+ () => {
231
+ const result = stringify(tokenizeString('0e-0'))
232
+ if (result !== '[{"kind":"number","value":"0e-0"}]') { throw result }
233
+ },
234
+ () => {
235
+ const result = stringify(tokenizeString('12e0000'))
236
+ if (result !== '[{"kind":"number","value":"12e0000"}]') { throw result }
237
+ },
238
+ () => {
239
+ const result = stringify(tokenizeString('-12e-0001'))
240
+ if (result !== '[{"kind":"number","value":"-12e-0001"}]') { throw result }
241
+ },
242
+ () => {
243
+ const result = stringify(tokenizeString('0e'))
244
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
245
+ },
246
+ () => {
247
+ const result = stringify(tokenizeString('0e-'))
248
+ if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
249
+ },
250
+ ],
251
+ stress: () => {
252
+ const n = 10_000
253
+ const list = countdown(n)
254
+ const result = tokenizer.tokenize(list)
255
+ //if (toArray(result).length !== 9929) { throw result }
256
+ }
257
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.495",
3
+ "version": "0.0.497",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "module.f.cjs",
6
6
  "scripts": {