functionalscript 0.0.522 → 0.0.524
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.
- package/fjson/tokenizer/module.f.cjs +29 -484
- package/fjson/tokenizer/test.f.cjs +34 -6
- package/js/tokenizer/module.f.cjs +688 -0
- package/js/tokenizer/test.f.cjs +354 -0
- package/json/tokenizer/module.f.cjs +25 -485
- package/json/tokenizer/test.f.cjs +6 -6
- package/package.json +1 -1
|
@@ -1,499 +1,44 @@
|
|
|
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
1
|
const list = require('../../types/list/module.f.cjs')
|
|
5
|
-
const
|
|
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
|
-
* */
|
|
2
|
+
const jsTokenizer = require('../../js/tokenizer/module.f.cjs')
|
|
62
3
|
|
|
63
4
|
/**
|
|
64
5
|
* @typedef {|
|
|
65
|
-
*
|
|
66
|
-
*
|
|
6
|
+
* jsTokenizer.SimpleToken |
|
|
7
|
+
* jsTokenizer.StringToken |
|
|
8
|
+
* jsTokenizer.NumberToken |
|
|
9
|
+
* jsTokenizer.ErrorToken |
|
|
10
|
+
* jsTokenizer.IdToken |
|
|
11
|
+
* jsTokenizer.BigIntToken
|
|
67
12
|
* } FjsonToken
|
|
68
13
|
*/
|
|
69
14
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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)
|
|
15
|
+
/** @type {(input: jsTokenizer.JsToken) => FjsonToken} */
|
|
16
|
+
const mapToken = input =>
|
|
17
|
+
{
|
|
18
|
+
switch(input.kind)
|
|
19
|
+
{
|
|
20
|
+
case "id":
|
|
21
|
+
case "bigint":
|
|
22
|
+
case "{":
|
|
23
|
+
case "}":
|
|
24
|
+
case ":":
|
|
25
|
+
case ",":
|
|
26
|
+
case "[":
|
|
27
|
+
case "]":
|
|
28
|
+
case "true":
|
|
29
|
+
case "false":
|
|
30
|
+
case "null":
|
|
31
|
+
case "string":
|
|
32
|
+
case "number":
|
|
33
|
+
case "error": return input
|
|
34
|
+
default: return { kind: "error", message: "invalid token" }
|
|
461
35
|
}
|
|
462
36
|
}
|
|
463
37
|
|
|
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
38
|
/** @type {(input: list.List<number>) => list.List<FjsonToken>} */
|
|
494
|
-
const tokenize = input =>
|
|
39
|
+
const tokenize = input => list.map(mapToken)(jsTokenizer.tokenize(input))
|
|
495
40
|
|
|
496
41
|
module.exports = {
|
|
497
42
|
/** @readonly */
|
|
498
43
|
tokenize
|
|
499
|
-
}
|
|
44
|
+
}
|
|
@@ -10,7 +10,7 @@ const tokenizeString = s => toArray(tokenizer.tokenize(encoding.stringToList(s))
|
|
|
10
10
|
const stringify = fjson.stringify(sort)
|
|
11
11
|
|
|
12
12
|
module.exports = {
|
|
13
|
-
|
|
13
|
+
fjson: [
|
|
14
14
|
() => {
|
|
15
15
|
const result = tokenizeString('')
|
|
16
16
|
if (result.length !== 0) { throw result }
|
|
@@ -145,7 +145,7 @@ module.exports = {
|
|
|
145
145
|
},
|
|
146
146
|
() => {
|
|
147
147
|
const result = stringify(tokenizeString('0abc,'))
|
|
148
|
-
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":","}]') { throw result }
|
|
148
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"abc"},{"kind":","}]') { throw result }
|
|
149
149
|
},
|
|
150
150
|
() => {
|
|
151
151
|
const result = stringify(tokenizeString('123456789012345678901234567890'))
|
|
@@ -169,7 +169,7 @@ module.exports = {
|
|
|
169
169
|
},
|
|
170
170
|
() => {
|
|
171
171
|
const result = stringify(tokenizeString('9a:'))
|
|
172
|
-
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":":"}]') { throw result }
|
|
172
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"a"},{"kind":":"}]') { throw result }
|
|
173
173
|
},
|
|
174
174
|
() => {
|
|
175
175
|
const result = stringify(tokenizeString('-10'))
|
|
@@ -269,11 +269,39 @@ module.exports = {
|
|
|
269
269
|
},
|
|
270
270
|
() => {
|
|
271
271
|
const result = stringify(tokenizeString('123_123'))
|
|
272
|
-
if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
|
|
272
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"_123"}]') { throw result }
|
|
273
273
|
},
|
|
274
|
-
|
|
274
|
+
() => {
|
|
275
275
|
const result = stringify(tokenizeString('123$123'))
|
|
276
|
-
if (result !== '[{"kind":"error","message":"invalid number"}]') { throw result }
|
|
276
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"$123"}]') { throw result }
|
|
277
|
+
},
|
|
278
|
+
() => {
|
|
279
|
+
const result = stringify(tokenizeString('1234567890n'))
|
|
280
|
+
if (result !== '[{"kind":"bigint","value":1234567890n}]') { throw result }
|
|
281
|
+
},
|
|
282
|
+
() => {
|
|
283
|
+
const result = stringify(tokenizeString('0n'))
|
|
284
|
+
if (result !== '[{"kind":"bigint","value":0n}]') { throw result }
|
|
285
|
+
},
|
|
286
|
+
() => {
|
|
287
|
+
const result = stringify(tokenizeString('[-1234567890n]'))
|
|
288
|
+
if (result !== '[{"kind":"["},{"kind":"bigint","value":-1234567890n},{"kind":"]"}]') { throw result }
|
|
289
|
+
},
|
|
290
|
+
() => {
|
|
291
|
+
const result = stringify(tokenizeString('123.456n'))
|
|
292
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"n"}]') { throw result }
|
|
293
|
+
},
|
|
294
|
+
() => {
|
|
295
|
+
const result = stringify(tokenizeString('123e456n'))
|
|
296
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"n"}]') { throw result }
|
|
297
|
+
},
|
|
298
|
+
() => {
|
|
299
|
+
const result = stringify(tokenizeString('1234567890na'))
|
|
300
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"a"}]') { throw result }
|
|
301
|
+
},
|
|
302
|
+
() => {
|
|
303
|
+
const result = stringify(tokenizeString('1234567890nn'))
|
|
304
|
+
if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"id","value":"n"}]') { throw result }
|
|
277
305
|
},
|
|
278
306
|
]
|
|
279
307
|
}
|