functionalscript 0.0.504 → 0.0.506

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,8 @@
1
+ # FJSON, FJS Object Notation
2
+
3
+ - additional types: bigint
4
+
5
+ ## Rules
6
+
7
+ - can serialize/deserialize without reading source code
8
+ - no function serialization/deserialization
@@ -0,0 +1,111 @@
1
+ const list = require('../types/list/module.f.cjs')
2
+ const { next, flat, reduce, map, empty } = list
3
+ const { concat } = require('../types/string/module.f.cjs')
4
+ const object = require('../types/object/module.f.cjs')
5
+ const { at } = object
6
+ const operator = require('../types/function/operator/module.f.cjs')
7
+ const { compose, fn } = require('../types/function/module.f.cjs')
8
+ const { entries } = Object
9
+ const { serialize: bigintSerialize } = require('../types/bigint/module.f.cjs')
10
+
11
+ /**
12
+ * @typedef {{
13
+ * readonly [k in string]: Unknown
14
+ * }} Object
15
+ */
16
+
17
+ /** @typedef {readonly Unknown[]} Array */
18
+
19
+ /** @typedef {Object|boolean|string|number|null|Array|bigint} Unknown */
20
+
21
+ const jsonStringify = JSON.stringify
22
+
23
+ /** @type {(_: string) => list.List<string>} */
24
+ const stringSerialize = input => [jsonStringify(input)]
25
+
26
+ /** @type {(_: number) => list.List<string>} */
27
+ const numberSerialize = input => [jsonStringify(input)]
28
+
29
+ const nullSerialize = ['null']
30
+
31
+ const trueSerialize = ['true']
32
+
33
+ const falseSerialize = ['false']
34
+
35
+ /** @type {(_: boolean) => list.List<string>} */
36
+ const boolSerialize = value => value ? trueSerialize : falseSerialize
37
+
38
+ const colon = [':']
39
+ const comma = [',']
40
+
41
+ /** @type {operator.Reduce<list.List<string>>} */
42
+ const joinOp = b => prior => flat([prior, comma, b])
43
+
44
+ /** @type {(input: list.List<list.List<string>>) => list.List<string>} */
45
+ const join = reduce(joinOp)(empty)
46
+
47
+ /** @type {(open: string) => (close: string) => (input: list.List<list.List<string>>) => list.List<string>} */
48
+ const wrap = open => close => {
49
+ const seqOpen = [open]
50
+ const seqClose = [close]
51
+ return input => flat([seqOpen, join(input), seqClose])
52
+ }
53
+
54
+ const objectWrap = wrap('{')('}')
55
+
56
+ const arrayWrap = wrap('[')(']')
57
+
58
+ /** @typedef {object.Entry<Unknown>} Entry*/
59
+
60
+ /** @typedef {(list.List<Entry>)} Entries */
61
+
62
+ /** @typedef {(entries: Entries) => Entries} MapEntries */
63
+
64
+ /** @type {(mapEntries: MapEntries) => (value: Unknown) => list.List<string>} */
65
+ const serialize = sort => {
66
+ /** @type {(kv: readonly[string, Unknown]) => list.List<string>} */
67
+ const propertySerialize = ([k, v]) => flat([
68
+ stringSerialize(k),
69
+ colon,
70
+ f(v)
71
+ ])
72
+ const mapPropertySerialize = map(propertySerialize)
73
+ /** @type {(object: Object) => list.List<string>} */
74
+ const objectSerialize = fn(entries)
75
+ .then(sort)
76
+ .then(mapPropertySerialize)
77
+ .then(objectWrap)
78
+ .result
79
+ /** @type {(value: Unknown) => list.List<string>} */
80
+ const f = value => {
81
+ switch (typeof value) {
82
+ case 'boolean': { return boolSerialize(value) }
83
+ case 'number': { return numberSerialize(value) }
84
+ case 'string': { return stringSerialize(value) }
85
+ case 'bigint': { return [bigintSerialize(value)] }
86
+ default: {
87
+ if (value === null) { return nullSerialize }
88
+ if (value instanceof Array) { return arraySerialize(value) }
89
+ return objectSerialize(value)
90
+ }
91
+ }
92
+ }
93
+ const arraySerialize = compose(map(f))(arrayWrap)
94
+ return f
95
+ }
96
+
97
+ /**
98
+ * The standard `JSON.stringify` rules determined by
99
+ * https://262.ecma-international.org/6.0/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
100
+ *
101
+ * @type {(mapEntries: MapEntries) => (value: Unknown) => string}
102
+ */
103
+ const stringify = sort => compose(serialize(sort))(concat)
104
+
105
+ module.exports = {
106
+
107
+ /** @readonly */
108
+ stringify,
109
+ /** @readonly */
110
+ serialize,
111
+ }
@@ -0,0 +1,65 @@
1
+ const json = require('../json/module.f.cjs')
2
+ const { sort } = require('../types/object/module.f.cjs')
3
+ const { identity } = require('../types/function/module.f.cjs')
4
+ const fjson= require('./module.f.cjs')
5
+
6
+ module.exports = {
7
+ stringify: [
8
+ {
9
+ sort: () => {
10
+ const r = json.setProperty("Hello")(['a'])({})
11
+ const x = fjson.stringify(sort)(r)
12
+ if (x !== '{"a":"Hello"}') { throw x }
13
+ },
14
+ identity: () => {
15
+ const x = fjson.stringify(identity)(json.setProperty("Hello")(['a'])({}))
16
+ if (x !== '{"a":"Hello"}') { throw x }
17
+ },
18
+ },
19
+ {
20
+ sort: () => {
21
+ const x = fjson.stringify(sort)(json.setProperty("Hello")(['a'])({ c: [], b: 12 }))
22
+ if (x !== '{"a":"Hello","b":12,"c":[]}') { throw x }
23
+ },
24
+ identity: () => {
25
+ const x = fjson.stringify(identity)(json.setProperty("Hello")(['a'])({ c: [], b: 12 }))
26
+ if (x !== '{"c":[],"b":12,"a":"Hello"}') { throw x }
27
+ },
28
+ },
29
+ {
30
+ sort: () => {
31
+ const _0 = { a: { y: [24] }, c: [], b: 12 }
32
+ const _1 = json.setProperty("Hello")(['a', 'x'])(_0)
33
+ const _2 = fjson.stringify(sort)(_1)
34
+ if (_2 !== '{"a":{"x":"Hello","y":[24]},"b":12,"c":[]}') { throw _2 }
35
+ },
36
+ identity: () => {
37
+ const _0 = { a: { y: [24] }, c: [], b: 12 }
38
+ const _1 = json.setProperty("Hello")(['a', 'x'])(_0)
39
+ const _2 = fjson.stringify(identity)(_1)
40
+ if (_2 !== '{"a":{"y":[24],"x":"Hello"},"c":[],"b":12}') { throw _2 }
41
+ }
42
+ },
43
+ {
44
+ stringify: () => {
45
+ const bi = 1234567890n
46
+ const result = fjson.stringify(sort)(bi)
47
+ if (result !== '1234567890n') { throw result }
48
+ }
49
+ },
50
+ {
51
+ stringify: () => {
52
+ const arr = [0n, 1, 2n]
53
+ const result = fjson.stringify(sort)(arr)
54
+ if (result !== '[0n,1,2n]') { throw result }
55
+ }
56
+ },
57
+ {
58
+ stringify: () => {
59
+ const obj = {"a": 0n, "b": 1, "c": 2n}
60
+ const result = fjson.stringify(sort)(obj)
61
+ if (result !== '{"a":0n,"b":1,"c":2n}') { throw result }
62
+ }
63
+ }
64
+ ]
65
+ }
@@ -5,6 +5,7 @@ const list = require('../../types/list/module.f.cjs')
5
5
  const _range = require('../../types/range/module.f.cjs')
6
6
  const { one } = _range
7
7
  const { empty, stateScan, flat, toArray, reduce: listReduce, scan } = list
8
+ const bigfloat = require('../../types/bigfloat/module.f.cjs')
8
9
  const { fromCharCode } = String
9
10
  const {
10
11
  range,
@@ -59,6 +60,7 @@ const {
59
60
  * @typedef {{
60
61
  * readonly kind: 'number'
61
62
  * readonly value: string
63
+ * readonly bf: bigfloat.BigFloat
62
64
  * }} NumberToken
63
65
  * */
64
66
 
@@ -148,9 +150,20 @@ const rangeCapitalAF = range('AF')
148
150
  * readonly kind: 'number',
149
151
  * readonly numberKind: '0' | '-' | 'int' | '.' | 'fractional' | 'e' | 'e+' | 'e-' | 'expDigits'
150
152
  * readonly value: string
153
+ * readonly b: ParseNumberBuffer
151
154
  * }} ParseNumberState
152
155
  */
153
156
 
157
+ /**
158
+ * @typedef {{
159
+ * readonly s: -1n | 1n
160
+ * readonly m: bigint
161
+ * readonly f: number
162
+ * readonly es: -1 | 1
163
+ * readonly e: number
164
+ * }} ParseNumberBuffer
165
+ */
166
+
154
167
  /** @typedef {{ readonly kind: 'invalidNumber'}} InvalidNumberState */
155
168
 
156
169
  /** @typedef {{ readonly kind: 'eof'}} EofState */
@@ -222,9 +235,30 @@ const create = def => a => {
222
235
  return v => c => x(c)(i)(v)(c)
223
236
  }
224
237
 
238
+ /** @type {(digit: number) => bigint} */
239
+ const digitToBigInt = d => BigInt(d - digit0)
240
+
241
+ /** @type {(digit: number) => ParseNumberBuffer} */
242
+ const startNumber = digit => ({ s: 1n, m: digitToBigInt(digit), f: 0, es: 1, e: 0 })
243
+
244
+ /** @type {ParseNumberBuffer} */
245
+ const startNegativeNumber = { s: -1n, m: 0n, f: 0, es: 1, e: 0 }
246
+
247
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
248
+ const addIntDigit = digit => b => ({ ... b, m: b.m * 10n + digitToBigInt(digit)})
249
+
250
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
251
+ const addFracDigit = digit => b => ({ ... b, m: b.m * 10n + digitToBigInt(digit), f: b.f - 1})
252
+
253
+ /** @type {(digit: number) => (b: ParseNumberBuffer) => ParseNumberBuffer} */
254
+ const addExpDigit = digit => b => ({ ... b, e: b.e * 10 + digit - digit0})
255
+
256
+ /** @type {(s: ParseNumberState) => NumberToken} */
257
+ const bufferToNumberToken = ({value, b}) => ({ kind: 'number', value: value, bf: [b.s * b.m, b.f + b.es * b.e] })
258
+
225
259
  /** @type {(state: InitialState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
226
260
  const initialStateOp = create(state => () => [[{ kind: 'error', message: 'unexpected character' }], state])([
227
- rangeFunc(rangeOneNine)(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: 'int' }]),
261
+ rangeFunc(rangeOneNine)(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNumber(input), numberKind: 'int' }]),
228
262
  rangeFunc(latinSmallLetterRange)(() => input => [empty, { kind: 'keyword', value: fromCharCode(input) }]),
229
263
  rangeSetFunc(rangeSetWhiteSpace)(state => () => [empty, state]),
230
264
  rangeFunc(one(leftCurlyBracket))(state => () => [[{ kind: '{' }], state]),
@@ -234,8 +268,8 @@ const initialStateOp = create(state => () => [[{ kind: 'error', message: 'unexpe
234
268
  rangeFunc(one(leftSquareBracket))(state => () => [[{ kind: '[' }], state]),
235
269
  rangeFunc(one(rightSquareBracket))(state => () => [[{ kind: ']' }], state]),
236
270
  rangeFunc(one(quotationMark))(() => () => [empty, { kind: 'string', value: '' }]),
237
- rangeFunc(one(digit0))(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: '0' }]),
238
- rangeFunc(one(hyphenMinus))(() => input => [empty, { kind: 'number', value: fromCharCode(input), numberKind: '-' }])
271
+ rangeFunc(one(digit0))(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNumber(input), numberKind: '0' }]),
272
+ rangeFunc(one(hyphenMinus))(() => input => [empty, { kind: 'number', value: fromCharCode(input), b: startNegativeNumber, numberKind: '-' }])
239
273
  ])
240
274
 
241
275
  /** @type {() => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
@@ -245,7 +279,7 @@ const invalidNumberToToken = () => input => tokenizeOp({ kind: 'invalidNumber' }
245
279
  const fullStopToToken = state => input => {
246
280
  switch (state.numberKind) {
247
281
  case '0':
248
- case 'int': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '.' }]
282
+ case 'int': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: '.' }]
249
283
  default: return tokenizeOp({ kind: 'invalidNumber' })(input)
250
284
  }
251
285
  }
@@ -254,12 +288,14 @@ const fullStopToToken = state => input => {
254
288
  const digit0ToToken = state => input => {
255
289
  switch (state.numberKind) {
256
290
  case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
257
- case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: '0' }]
258
- case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
291
+ case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: '0' }]
292
+ case '.':
293
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addFracDigit(input)(state.b), numberKind: 'fractional' }]
259
294
  case 'e':
260
295
  case 'e+':
261
- case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
262
- default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
296
+ case 'e-':
297
+ case 'expDigits': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addExpDigit(input)(state.b), numberKind: 'expDigits' }]
298
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addIntDigit(input)(state.b), numberKind: state.numberKind }]
263
299
  }
264
300
  }
265
301
 
@@ -267,12 +303,13 @@ const digit0ToToken = state => input => {
267
303
  const digit19ToToken = state => input => {
268
304
  switch (state.numberKind) {
269
305
  case '0': return tokenizeOp({ kind: 'invalidNumber' })(input)
270
- case '-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'int' }]
271
- case '.': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'fractional' }]
306
+ case '.':
307
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addFracDigit(input)(state.b), numberKind: 'fractional' }]
272
308
  case 'e':
273
309
  case 'e+':
274
- case 'e-': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'expDigits' }]
275
- default: return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: state.numberKind }]
310
+ case 'e-':
311
+ case 'expDigits': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addExpDigit(input)(state.b), numberKind: 'expDigits' }]
312
+ default: return [empty, { kind: 'number', value: appendChar(state.value)(input), b: addIntDigit(input)(state.b), numberKind: 'int' }]
276
313
  }
277
314
  }
278
315
 
@@ -281,7 +318,7 @@ const expToToken = state => input => {
281
318
  switch (state.numberKind) {
282
319
  case '0':
283
320
  case 'int':
284
- case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e' }]
321
+ case 'fractional': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: 'e' }]
285
322
  default: return tokenizeOp({ kind: 'invalidNumber' })(input)
286
323
  }
287
324
  }
@@ -289,7 +326,7 @@ const expToToken = state => input => {
289
326
  /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
290
327
  const hyphenMinusToToken = state => input => {
291
328
  switch (state.numberKind) {
292
- case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e-' }]
329
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: { ... state.b, es: -1}, numberKind: 'e-' }]
293
330
  default: return tokenizeOp({ kind: 'invalidNumber' })(input)
294
331
  }
295
332
  }
@@ -297,7 +334,7 @@ const hyphenMinusToToken = state => input => {
297
334
  /** @type {(state: ParseNumberState) => (input: number) => readonly[list.List<JsonToken>, TokenizerState]} */
298
335
  const plusSignToToken = state => input => {
299
336
  switch (state.numberKind) {
300
- case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), numberKind: 'e+' }]
337
+ case 'e': return [empty, { kind: 'number', value: appendChar(state.value)(input), b: state.b, numberKind: 'e+' }]
301
338
  default: return tokenizeOp({ kind: 'invalidNumber' })(input)
302
339
  }
303
340
  }
@@ -317,7 +354,7 @@ const terminalToToken = state => input => {
317
354
  default:
318
355
  {
319
356
  const next = tokenizeOp({ kind: 'initial' })(input)
320
- return [{ first: { kind: 'number', value: state.value }, tail: next[0] }, next[1]]
357
+ return [{ first: bufferToNumberToken(state), tail: next[0] }, next[1]]
321
358
  }
322
359
  }
323
360
  }
@@ -441,7 +478,7 @@ const tokenizeEofOp = state => {
441
478
  case 'e':
442
479
  case 'e+':
443
480
  case 'e-': return [[{ kind: 'error', message: 'invalid number' }], { kind: 'invalidNumber', }]
444
- default: return [[{ kind: 'number', value: state.value }], { kind: 'eof' }]
481
+ default: return [[bufferToNumberToken(state)], { kind: 'eof' }]
445
482
  }
446
483
  case 'eof': return [[{ kind: 'error', message: 'eof' }], state]
447
484
  }
@@ -1,13 +1,13 @@
1
1
  const tokenizer = require('./module.f.cjs')
2
2
  const { toArray, countdown } = require('../../types/list/module.f.cjs')
3
- const json = require('../module.f.cjs')
3
+ const fjson = require('../../fjson/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
8
  const tokenizeString = s => toArray(tokenizer.tokenize(encoding.stringToList(s)))
9
9
 
10
- const stringify = json.stringify(sort)
10
+ const stringify = fjson.stringify(sort)
11
11
 
12
12
  module.exports = {
13
13
  testing: [
@@ -133,11 +133,11 @@ module.exports = {
133
133
  },
134
134
  () => {
135
135
  const result = stringify(tokenizeString('0'))
136
- if (result !== '[{"kind":"number","value":"0"}]') { throw result }
136
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"0"}]') { throw result }
137
137
  },
138
138
  () => {
139
139
  const result = stringify(tokenizeString('[0]'))
140
- if (result !== '[{"kind":"["},{"kind":"number","value":"0"},{"kind":"]"}]') { throw result }
140
+ if (result !== '[{"kind":"["},{"bf":[0n,0],"kind":"number","value":"0"},{"kind":"]"}]') { throw result }
141
141
  },
142
142
  () => {
143
143
  const result = stringify(tokenizeString('00'))
@@ -148,20 +148,20 @@ module.exports = {
148
148
  if (result !== '[{"kind":"error","message":"invalid number"},{"kind":","}]') { throw result }
149
149
  },
150
150
  () => {
151
- const result = stringify(tokenizeString('1234567890'))
152
- if (result !== '[{"kind":"number","value":"1234567890"}]') { throw result }
151
+ const result = stringify(tokenizeString('123456789012345678901234567890'))
152
+ if (result !== '[{"bf":[123456789012345678901234567890n,0],"kind":"number","value":"123456789012345678901234567890"}]') { throw result }
153
153
  },
154
154
  () => {
155
155
  const result = stringify(tokenizeString('{90}'))
156
- if (result !== '[{"kind":"{"},{"kind":"number","value":"90"},{"kind":"}"}]') { throw result }
156
+ if (result !== '[{"kind":"{"},{"bf":[90n,0],"kind":"number","value":"90"},{"kind":"}"}]') { throw result }
157
157
  },
158
158
  () => {
159
159
  const result = stringify(tokenizeString('1 2'))
160
- if (result !== '[{"kind":"number","value":"1"},{"kind":"number","value":"2"}]') { throw result }
160
+ if (result !== '[{"bf":[1n,0],"kind":"number","value":"1"},{"bf":[2n,0],"kind":"number","value":"2"}]') { throw result }
161
161
  },
162
162
  () => {
163
163
  const result = stringify(tokenizeString('0. 2'))
164
- if (result !== '[{"kind":"error","message":"invalid number"},{"kind":"number","value":"2"}]') { throw result }
164
+ if (result !== '[{"kind":"error","message":"invalid number"},{"bf":[2n,0],"kind":"number","value":"2"}]') { throw result }
165
165
  },
166
166
  () => {
167
167
  const result = stringify(tokenizeString('10-0'))
@@ -173,11 +173,11 @@ module.exports = {
173
173
  },
174
174
  () => {
175
175
  const result = stringify(tokenizeString('-10'))
176
- if (result !== '[{"kind":"number","value":"-10"}]') { throw result }
176
+ if (result !== '[{"bf":[-10n,0],"kind":"number","value":"-10"}]') { throw result }
177
177
  },
178
178
  () => {
179
179
  const result = stringify(tokenizeString('-0'))
180
- if (result !== '[{"kind":"number","value":"-0"}]') { throw result }
180
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"-0"}]') { throw result }
181
181
  },
182
182
  () => {
183
183
  const result = stringify(tokenizeString('-00'))
@@ -189,11 +189,11 @@ module.exports = {
189
189
  },
190
190
  () => {
191
191
  const result = stringify(tokenizeString('0.01'))
192
- if (result !== '[{"kind":"number","value":"0.01"}]') { throw result }
192
+ if (result !== '[{"bf":[1n,-2],"kind":"number","value":"0.01"}]') { throw result }
193
193
  },
194
194
  () => {
195
195
  const result = stringify(tokenizeString('-0.9'))
196
- if (result !== '[{"kind":"number","value":"-0.9"}]') { throw result }
196
+ if (result !== '[{"bf":[-9n,-1],"kind":"number","value":"-0.9"}]') { throw result }
197
197
  },
198
198
  () => {
199
199
  const result = stringify(tokenizeString('-0.'))
@@ -205,11 +205,11 @@ module.exports = {
205
205
  },
206
206
  () => {
207
207
  const result = stringify(tokenizeString('12.34'))
208
- if (result !== '[{"kind":"number","value":"12.34"}]') { throw result }
208
+ if (result !== '[{"bf":[1234n,-2],"kind":"number","value":"12.34"}]') { throw result }
209
209
  },
210
210
  () => {
211
211
  const result = stringify(tokenizeString('-12.00'))
212
- if (result !== '[{"kind":"number","value":"-12.00"}]') { throw result }
212
+ if (result !== '[{"bf":[-1200n,-2],"kind":"number","value":"-12.00"}]') { throw result }
213
213
  },
214
214
  () => {
215
215
  const result = stringify(tokenizeString('-12.'))
@@ -221,23 +221,27 @@ module.exports = {
221
221
  },
222
222
  () => {
223
223
  const result = stringify(tokenizeString('0e1'))
224
- if (result !== '[{"kind":"number","value":"0e1"}]') { throw result }
224
+ if (result !== '[{"bf":[0n,1],"kind":"number","value":"0e1"}]') { throw result }
225
225
  },
226
226
  () => {
227
227
  const result = stringify(tokenizeString('0e+2'))
228
- if (result !== '[{"kind":"number","value":"0e+2"}]') { throw result }
228
+ if (result !== '[{"bf":[0n,2],"kind":"number","value":"0e+2"}]') { throw result }
229
229
  },
230
230
  () => {
231
231
  const result = stringify(tokenizeString('0e-0'))
232
- if (result !== '[{"kind":"number","value":"0e-0"}]') { throw result }
232
+ if (result !== '[{"bf":[0n,0],"kind":"number","value":"0e-0"}]') { throw result }
233
233
  },
234
234
  () => {
235
235
  const result = stringify(tokenizeString('12e0000'))
236
- if (result !== '[{"kind":"number","value":"12e0000"}]') { throw result }
236
+ if (result !== '[{"bf":[12n,0],"kind":"number","value":"12e0000"}]') { throw result }
237
237
  },
238
238
  () => {
239
239
  const result = stringify(tokenizeString('-12e-0001'))
240
- if (result !== '[{"kind":"number","value":"-12e-0001"}]') { throw result }
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 }
241
245
  },
242
246
  () => {
243
247
  const result = stringify(tokenizeString('0e'))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.0.504",
3
+ "version": "0.0.506",
4
4
  "description": "FunctionalScript is a functional subset of JavaScript",
5
5
  "main": "module.f.cjs",
6
6
  "scripts": {
@@ -13,6 +13,9 @@ const abs = a => a >= 0 ? a : -a
13
13
  /** @type {(a: bigint) => compare.Sign} */
14
14
  const sign = a => unsafeCmp(a)(0n)
15
15
 
16
+ /** @type {(a: bigint) => string} */
17
+ const serialize = a => `${a}n`
18
+
16
19
  module.exports = {
17
20
  /** @readonly */
18
21
  addition,
@@ -22,4 +25,6 @@ module.exports = {
22
25
  abs,
23
26
  /** @readonly */
24
27
  sign,
28
+ /** @readonly */
29
+ serialize,
25
30
  }
@@ -1,4 +1,4 @@
1
- const { sum, abs } = require('./module.f.cjs')
1
+ const { sum, abs, serialize } = require('./module.f.cjs')
2
2
 
3
3
  module.exports = {
4
4
  sum: () => {
@@ -14,5 +14,19 @@ module.exports = {
14
14
  const result = abs(-10n)
15
15
  if (result !== 10n) { throw result }
16
16
  }
17
+ ],
18
+ serialize: [
19
+ () => {
20
+ const result = serialize(0n)
21
+ if (result !== '0n') { throw result }
22
+ },
23
+ () => {
24
+ const result = serialize(123456789012345678901234567890n)
25
+ if (result !== '123456789012345678901234567890n') { throw result }
26
+ },
27
+ () => {
28
+ const result = serialize(-55555n)
29
+ if (result !== '-55555n') { throw result }
30
+ },
17
31
  ]
18
32
  }