functionalscript 0.4.0 → 0.4.1

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/bnf/djs/test.f.js CHANGED
@@ -1,4 +1,4 @@
1
- import { cp, range, remove, str } from "../func/module.f.js";
1
+ import { cp, range, remove, set, str } from "../func/module.f.js";
2
2
  import { parser, toRuleMap } from "./module.f.js";
3
3
  import { classic } from "../func/testlib.f.js";
4
4
  import * as j from "../../json/module.f.js";
@@ -114,14 +114,24 @@ const classicTest = () => {
114
114
  const result = map;
115
115
  return result;
116
116
  };
117
+ const repeat0Name = (v) => `${v}Repeat0`;
118
+ const repeat0Body = (v) => [
119
+ [],
120
+ [v, repeat0Name(v)]
121
+ ];
122
+ const repeat0 = (v) => {
123
+ const name = repeat0Name(v);
124
+ const body = repeat0Body(v);
125
+ return { [name]: body };
126
+ };
117
127
  const deterministic = () => {
118
128
  const map = {
119
129
  json: [
120
- ['ws', 'element']
130
+ ['wsRepeat0', 'element']
121
131
  ],
122
132
  value: [
123
- [cp('{'), 'ws', 'object', cp('}')],
124
- [cp('['), 'ws', 'array', cp(']')],
133
+ [cp('{'), 'wsRepeat0', 'object', cp('}')],
134
+ [cp('['), 'wsRepeat0', 'array', cp(']')],
125
135
  ['string'],
126
136
  ['number'],
127
137
  str('true'),
@@ -130,14 +140,14 @@ const deterministic = () => {
130
140
  ],
131
141
  object: [
132
142
  [],
133
- ['member', 'members'],
143
+ ['member', 'memberTailRepeat0'],
134
144
  ],
135
- members: [
136
- [],
137
- [cp(','), 'ws', 'member', 'members'],
145
+ memberTail: [
146
+ [cp(','), 'wsRepeat0', 'member']
138
147
  ],
148
+ ...repeat0('memberTail'),
139
149
  member: [
140
- ['string', 'ws', cp(':'), 'ws', 'element'],
150
+ ['string', 'wsRepeat0', cp(':'), 'wsRepeat0', 'element'],
141
151
  ],
142
152
  array: [
143
153
  [],
@@ -145,31 +155,21 @@ const deterministic = () => {
145
155
  ],
146
156
  elements: [
147
157
  [],
148
- [cp(','), 'ws', 'element', 'elements'],
158
+ [cp(','), 'wsRepeat0', 'element', 'elements'],
149
159
  ],
150
160
  element: [
151
- ['value', 'ws'],
161
+ ['value', 'wsRepeat0'],
152
162
  ],
153
163
  string: [
154
- [cp('"'), 'characters', cp('"')],
155
- ],
156
- characters: [
157
- [],
158
- ['character', 'characters'],
164
+ [cp('"'), 'characterRepeat0', cp('"')],
159
165
  ],
166
+ ...repeat0('character'),
160
167
  character: [
161
168
  ...remove([0x20, 0x10FFFF], [cp('"'), cp('\\')]),
162
169
  [cp('\\'), 'escape'], // 92
163
170
  ],
164
171
  escape: [
165
- [cp('"')],
166
- [cp('\\')],
167
- [cp('/')],
168
- [cp('b')],
169
- [cp('f')],
170
- [cp('n')],
171
- [cp('r')],
172
- [cp('t')],
172
+ ...set('"\\/bfnrt'),
173
173
  [cp('u'), 'hex', 'hex', 'hex', 'hex'],
174
174
  ],
175
175
  hex: [
@@ -177,18 +177,18 @@ const deterministic = () => {
177
177
  [range('AF')],
178
178
  [range('af')],
179
179
  ],
180
+ numberSign: [
181
+ [],
182
+ [cp('-')]
183
+ ],
180
184
  number: [
181
- ['integer', 'fraction', 'exponent'],
182
- [cp('-'), 'integer', 'fraction', 'exponent'],
185
+ ['numberSign', 'integer', 'fraction', 'exponent'],
183
186
  ],
184
187
  integer: [
185
188
  [cp('0')],
186
- ['onenine', 'digits'],
187
- ],
188
- digits: [
189
- [],
190
- ['digit', 'digits'],
189
+ ['onenine', 'digitRepeat0'],
191
190
  ],
191
+ ...repeat0('digit'),
192
192
  digit: [
193
193
  [cp('0')],
194
194
  ['onenine'],
@@ -198,25 +198,20 @@ const deterministic = () => {
198
198
  ],
199
199
  fraction: [
200
200
  [],
201
- [cp('.'), 'digit', 'digits'],
201
+ [cp('.'), 'digit', 'digitRepeat0'],
202
202
  ],
203
+ e: set('Ee'),
203
204
  exponent: [
204
205
  [],
205
- [cp('E'), 'sign', 'digit', 'digits'],
206
- [cp('e'), 'sign', 'digit', 'digits'],
206
+ ['e', 'sign', 'digit', 'digitRepeat0'],
207
207
  ],
208
208
  sign: [
209
209
  [],
210
210
  [cp('+')],
211
211
  [cp('-')],
212
212
  ],
213
- ws: [
214
- [],
215
- [cp(' '), 'ws'],
216
- [cp('\n'), 'ws'],
217
- [cp('\r'), 'ws'],
218
- [cp('\t'), 'ws'],
219
- ],
213
+ ws: set(' \n\r\t'),
214
+ ...repeat0('ws'),
220
215
  };
221
216
  const _map = map;
222
217
  return _map;
@@ -1,15 +1,13 @@
1
1
  /**
2
- * Types for defining language grammar using Backus-Naur Form (BNF).
3
- *
4
- * @module
5
- *
6
- * @description
2
+ * Types for defining language grammar using Backus-Naur Form (BNF).
7
3
  *
8
4
  * Utilities for serializing and deserializing BNF grammar
9
5
  * and creating a simple LL(1) parser.
10
6
  *
11
7
  * See [Backus-Naur form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form).
12
8
  *
9
+ * @module
10
+ *
13
11
  * @example
14
12
  *
15
13
  * ```ts
@@ -124,6 +122,13 @@ type RangeSet = readonly TerminalRange[];
124
122
  * A set of terminal ranges compatible with the `Or` rule.
125
123
  */
126
124
  export type OrRangeSet = readonly (readonly [TerminalRange])[];
125
+ /**
126
+ * Convert a sequence of character into `OrRangeSet`
127
+ *
128
+ * @param s a set of code points
129
+ * @returns A set compatible with `Or`
130
+ */
131
+ export declare const set: (s: string) => OrRangeSet;
127
132
  /**
128
133
  * Removes a terminal range from a set of ranges.
129
134
  *
@@ -138,4 +143,6 @@ export type OrRangeSet = readonly (readonly [TerminalRange])[];
138
143
  * ```
139
144
  */
140
145
  export declare const remove: (range: TerminalRange, removeSet: RangeSet) => OrRangeSet;
146
+ export declare const repeat0: (rule: Rule) => Rule;
147
+ export declare const join0: (rule: Rule, separator: Rule) => Rule;
141
148
  export {};
@@ -1,15 +1,13 @@
1
1
  /**
2
- * Types for defining language grammar using Backus-Naur Form (BNF).
3
- *
4
- * @module
5
- *
6
- * @description
2
+ * Types for defining language grammar using Backus-Naur Form (BNF).
7
3
  *
8
4
  * Utilities for serializing and deserializing BNF grammar
9
5
  * and creating a simple LL(1) parser.
10
6
  *
11
7
  * See [Backus-Naur form](https://en.wikipedia.org/wiki/Backus%E2%80%93Naur_form).
12
8
  *
9
+ * @module
10
+ *
13
11
  * @example
14
12
  *
15
13
  * ```ts
@@ -73,6 +71,14 @@ export const range = (ab) => {
73
71
  // deno-lint-ignore no-explicit-any
74
72
  return a;
75
73
  };
74
+ const toOr = (r) => r.map(v => [v]);
75
+ /**
76
+ * Convert a sequence of character into `OrRangeSet`
77
+ *
78
+ * @param s a set of code points
79
+ * @returns A set compatible with `Or`
80
+ */
81
+ export const set = (s) => toOr(str(s));
76
82
  const removeOne = (set, [a, b]) => {
77
83
  let result = [];
78
84
  for (const [a0, b0] of set) {
@@ -107,5 +113,20 @@ export const remove = (range, removeSet) => {
107
113
  for (const r of removeSet) {
108
114
  result = removeOne(result, r);
109
115
  }
110
- return result.map(v => [v]);
116
+ return toOr(result);
117
+ };
118
+ export const repeat0 = (rule) => {
119
+ const result = () => [
120
+ [],
121
+ [rule, result],
122
+ ];
123
+ return result;
124
+ };
125
+ export const join0 = (rule, separator) => {
126
+ const tail = repeat0(() => [[separator, rule]]);
127
+ const result = () => [
128
+ [],
129
+ [rule, tail],
130
+ ];
131
+ return result;
111
132
  };
@@ -1,20 +1,14 @@
1
1
  import { stringify } from "../../json/module.f.js";
2
2
  import { sort } from "../../types/object/module.f.js";
3
- import { cp, str, range, remove, } from "./module.f.js";
3
+ import { cp, str, range, remove, set, repeat0, join0, } from "./module.f.js";
4
4
  const deterministic = () => {
5
+ const wsSet = () => set('\t\n\r '); // 9,10,13,32
5
6
  // {"empty":true,"map":[[false,8],[true,10],[false,12],[true,13],[false,31],[true,32]]}
6
- const ws = () => [
7
- [],
8
- [cp('\t'), ws], // 9
9
- [cp('\n'), ws], // 10
10
- [cp('\r'), ws], // 13
11
- [cp(' '), ws], // 32
12
- ];
7
+ const ws = repeat0(wsSet);
13
8
  // {"empty":true,"map":[[false,42],[true,43],[false,44],[true,45]]}
14
9
  const sign = () => [
15
10
  [],
16
- str('+'), // 43
17
- str('-'), // 45
11
+ ...set('+-'), // 43
18
12
  ];
19
13
  // {"empty":false,"map":[[false,48],[true,57]]}
20
14
  const onenine = () => [
@@ -26,34 +20,31 @@ const deterministic = () => {
26
20
  [onenine],
27
21
  ];
28
22
  // {"empty":true,"map":[[false,47],[true,57]]}
29
- const digits1 = () => [
30
- [],
31
- [digit, digits1]
32
- ];
23
+ const digits0 = repeat0(digit);
33
24
  // {"empty":false,"map":[[false,47],[true,57]]}
34
- const digits = () => [
35
- [digit, digits1]
25
+ const digits1 = () => [
26
+ [digit, digits0]
36
27
  ];
28
+ const e = () => set('Ee'); // 69, 101
37
29
  // {"empty":true,"map":[[false,68],[true,69],[false,100],[true,101]]}
38
30
  const exponent = () => [
39
31
  [],
40
- [cp('E'), sign, digits], // 69
41
- [cp('e'), sign, digits], // 101
32
+ [e, sign, digits1], // 69 | 101
42
33
  ];
43
34
  // {"empty":true,"map":[[false,45],[true,46]]}
44
35
  const fraction = () => [
45
36
  [],
46
- [cp('.'), digits] // 46
37
+ [cp('.'), digits1] // 46
47
38
  ];
48
39
  // {"empty":false,"map":[[false,47],[true,57]]}
49
- const integer1 = () => [
40
+ const unsignedInteger = () => [
50
41
  str('0'), // 48
51
- [onenine, digits1], // 49-57
42
+ [onenine, digits0], // 49-57
52
43
  ];
53
44
  // {"empty":false,"map":[[false,44],[true,45],[false,47],[true,57]]}
54
45
  const integer = () => [
55
- [integer1], // 48-57
56
- [cp('-'), integer1], // 45
46
+ [unsignedInteger], // 48-57
47
+ [cp('-'), unsignedInteger], // 45
57
48
  ];
58
49
  // {"empty":false,"map":[[false,44],[true,45],[false,47],[true,57]]}
59
50
  const number = () => [
@@ -67,14 +58,7 @@ const deterministic = () => {
67
58
  ];
68
59
  // {"empty":false,"map":[[false,33],[true,34],[false,46],[true,47],[false,91],[true,92],[false,97],[true,98],[false,101],[true,102],[false,109],[true,110],[false,113],[true,114],[false,115],[true,117]]}
69
60
  const escape = () => [
70
- str('"'), // 34
71
- str('/'), // 47
72
- str('\\'), // 92
73
- str('b'), // 98
74
- str('f'), // 102
75
- str('n'), // 110
76
- str('r'), // 114
77
- str('t'), // 116
61
+ ...set('"\\/bfnrt'),
78
62
  [cp('u'), hex, hex, hex, hex] // 117
79
63
  ];
80
64
  // {"empty":false,"map":[[false,31],[true,33],[false,34],[true,1114111]]}
@@ -83,70 +67,27 @@ const deterministic = () => {
83
67
  [cp('\\'), escape], // 92
84
68
  ];
85
69
  // {"empty":true,"map":[[false,31],[true,33],[false,34],[true,1114111]]}
86
- const characters = () => [
87
- [],
88
- [character, characters]
89
- ];
70
+ const characters = repeat0(character);
90
71
  // {"empty":false,"map":[[false,33],[true,34]]}
91
72
  const string = () => [
92
73
  [cp('"'), characters, cp('"')]
93
74
  ];
75
+ const comma = () => [[cp(','), ws]];
94
76
  // {"empty":false,"map":[[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
95
- const element1 = () => [
96
- [value, ws]
97
- ];
98
- // {"empty":false,"map":[[false,8],[true,10],[false,12],[true,13],[false,31],[true,32],[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
99
77
  const element = () => [
100
- [ws, element1]
101
- ];
102
- // {"empty":true,"map":[[false,43],[true,44]]}
103
- const elements2 = () => [
104
- [],
105
- [cp(','), elements] // 44
106
- ];
107
- // {"empty":false,"map":[[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
108
- const elements1 = () => [
109
- [element1, elements2]
110
- ];
111
- // {"empty":false,"map":[[false,8],[true,10],[false,12],[true,13],[false,31],[true,32],[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
112
- const elements = () => [
113
- [ws, elements1]
78
+ [value, ws]
114
79
  ];
115
- // {"empty":false,"map":[[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,92],[true,93],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
116
- const array1 = () => [
117
- str(']'), // 93
118
- [elements1, cp(']')],
80
+ const list = (open, rule, close) => () => [
81
+ [cp(open), ws, join0(rule, comma), cp(close)]
119
82
  ];
120
83
  // {"empty":false,"map":[[false,90],[true,91]]}
121
- const array = () => [
122
- [cp('['), ws, array1]
123
- ];
84
+ const array = list('[', element, ']');
124
85
  // {"empty":false,"map":[[false,33],[true,34]]}
125
- const member1 = () => [
126
- [string, ws, cp(':'), element]
127
- ];
128
- // {"empty":true,"map":[[false,43],[true,44]]}
129
- const members2 = () => [
130
- [],
131
- [cp(','), members], // 44
132
- ];
133
- // {"empty":false,"map":[[false,33],[true,34]]}
134
- const members1 = () => [
135
- [member1, members2]
136
- ];
137
- // {"empty":false,"map":[[false,8],[true,10],[false,12],[true,13],[false,31],[true,32],[false,33],[true,34]]}
138
- const members = () => [
139
- [ws, members1]
140
- ];
141
- // {"empty":false,"map":[[false,33],[true,34],[false,124],[true,125]]}
142
- const object1 = () => [
143
- str('}'), // 125
144
- [members1, cp('}')],
86
+ const member = () => [
87
+ [string, ws, cp(':'), ws, element]
145
88
  ];
146
89
  // {"empty":false,"map":[[false,122],[true,123]]}
147
- const object = () => [
148
- [cp('{'), ws, object1]
149
- ];
90
+ const object = list('{', member, '}');
150
91
  // {"empty":false,"map":[[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
151
92
  const value = () => [
152
93
  [object], // 123
@@ -158,7 +99,7 @@ const deterministic = () => {
158
99
  str('null'), // 110
159
100
  ];
160
101
  // {"empty":false,"map":[[false,8],[true,10],[false,12],[true,13],[false,31],[true,32],[false,33],[true,34],[false,44],[true,45],[false,47],[true,57],[false,90],[true,91],[false,101],[true,102],[false,109],[true,110],[false,115],[true,116],[false,122],[true,123]]}
161
- const json = element;
102
+ const json = [ws, element];
162
103
  const _ = json;
163
104
  };
164
105
  //
@@ -0,0 +1,30 @@
1
+ import { type Array2 } from '../../types/array/module.f.ts';
2
+ export type Range = bigint;
3
+ export type Sequence = readonly Rule[];
4
+ export type Or = {
5
+ readonly [k in string]: Rule;
6
+ };
7
+ export type DataRule = Or | Sequence | Range | string;
8
+ export type LazyRule = () => DataRule;
9
+ export type Rule = DataRule | LazyRule;
10
+ export declare const rangeEncode: (a: number, b: number) => Range;
11
+ export declare const oneEncode: (a: number) => Range;
12
+ export declare const rangeDecode: (r: bigint) => Array2<number>;
13
+ export declare const str: (s: string) => readonly Range[] | Range;
14
+ export declare const set: (s: string) => Or;
15
+ export declare const range: (ab: string) => Range;
16
+ export type None = readonly [];
17
+ export declare const none: None;
18
+ export type Option<S> = {
19
+ none: None;
20
+ some: S;
21
+ };
22
+ export declare const option: <S extends Rule>(some: S) => Option<S>;
23
+ export type Repeat0<T> = () => Option<readonly [T, Repeat0<T>]>;
24
+ export declare const repeat0: <T extends Rule>(some: T) => Repeat0<T>;
25
+ export type Repeat1<T> = readonly [T, Repeat0<T>];
26
+ export declare const repeat1: <T extends Rule>(some: T) => Repeat1<T>;
27
+ export type Join1<T, S> = readonly [T, Repeat0<readonly [S, T]>];
28
+ export declare const join1: <T extends Rule, S extends Rule>(some: T, separator: S) => Join1<T, S>;
29
+ export type Join0<T, S> = Option<readonly [T, Repeat0<readonly [S, T]>]>;
30
+ export declare const join0: <T extends Rule, S extends Rule>(some: T, separator: S) => Rule;
@@ -0,0 +1,37 @@
1
+ import { stringToCodePointList } from "../../text/utf16/module.f.js";
2
+ import { isArray2 } from "../../types/array/module.f.js";
3
+ import { map, toArray } from "../../types/list/module.f.js";
4
+ //
5
+ const { fromEntries } = Object;
6
+ const { fromCodePoint } = String;
7
+ const offset = 24n;
8
+ const mask = (1n << offset) - 1n;
9
+ export const rangeEncode = (a, b) => (BigInt(a) << offset) | BigInt(b);
10
+ export const oneEncode = (a) => rangeEncode(a, a);
11
+ export const rangeDecode = (r) => [Number(r >> offset), Number(r & mask)];
12
+ const mapOneEncode = map(oneEncode);
13
+ export const str = (s) => {
14
+ const x = toArray(mapOneEncode(stringToCodePointList(s)));
15
+ return x.length === 1 ? x[0] : x;
16
+ };
17
+ const mapEntry = map((v) => [fromCodePoint(v), oneEncode(v)]);
18
+ export const set = (s) => fromEntries(toArray(mapEntry(stringToCodePointList(s))));
19
+ export const range = (ab) => {
20
+ const a = toArray(stringToCodePointList(ab));
21
+ if (!isArray2(a)) {
22
+ throw `Invalid range ${ab}.`;
23
+ }
24
+ return rangeEncode(...a);
25
+ };
26
+ export const none = [];
27
+ export const option = (some) => ({
28
+ none,
29
+ some,
30
+ });
31
+ export const repeat0 = (some) => {
32
+ const f = () => option([some, f]);
33
+ return f;
34
+ };
35
+ export const repeat1 = (some) => [some, repeat0(some)];
36
+ export const join1 = (some, separator) => [some, repeat0([separator, some])];
37
+ export const join0 = (some, separator) => option(join1(some, separator));
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,138 @@
1
+ import { join0, none, option, range, repeat0, set } from "./module.f.js";
2
+ const _classic = () => {
3
+ const json = () => [element];
4
+ const value = () => ({
5
+ object,
6
+ array,
7
+ string,
8
+ number,
9
+ true: 'true',
10
+ false: 'false',
11
+ null: 'null'
12
+ });
13
+ const object = () => ({
14
+ ws: ['{', ws, '}'],
15
+ members: ['{', members, '}'],
16
+ });
17
+ const members = () => ({
18
+ member,
19
+ members: [member, ',', members],
20
+ });
21
+ const member = () => [ws, string, ws, ':', element];
22
+ const array = () => ({
23
+ ws: ['[', ws, ']'],
24
+ elements: ['[', elements, ']'],
25
+ });
26
+ const elements = () => ({
27
+ element,
28
+ elements: [element, ',', elements],
29
+ });
30
+ const element = () => [ws, value, ws];
31
+ const string = () => ['"', characters, '"'];
32
+ const characters = () => ({
33
+ none,
34
+ characters: [character, characters],
35
+ });
36
+ const character = () => ({
37
+ 0: 0x20000021n,
38
+ 1: 0x2300005bn,
39
+ 2: 0x5d10ffffn,
40
+ escape: ['\\', escape],
41
+ });
42
+ const escape = () => ({
43
+ ...set('"\\/bfnrt'),
44
+ u: ['u', hex, hex, hex, hex],
45
+ });
46
+ const hex = () => ({
47
+ digit,
48
+ AF: range('AF'),
49
+ af: range('af'),
50
+ });
51
+ const number = () => [integer, fraction, exponent];
52
+ const integer = () => ({
53
+ digit,
54
+ onenine: [onenine, digits],
55
+ negDigit: ['-', digit],
56
+ negOnenine: ['-', onenine, digits],
57
+ });
58
+ const digits = () => ({
59
+ digit,
60
+ digits: [digit, digits],
61
+ });
62
+ const digit = () => ({
63
+ '0': '0',
64
+ onenine,
65
+ });
66
+ const onenine = range('12');
67
+ const fraction = () => ({
68
+ none,
69
+ digits: ['.', digits],
70
+ });
71
+ const exponent = () => ({
72
+ none,
73
+ E: ['E', sign, digits],
74
+ e: ['e', sign, digits],
75
+ });
76
+ const sign = {
77
+ none,
78
+ ...set('+-'),
79
+ };
80
+ const ws = () => ({
81
+ none,
82
+ ' ': [' ', ws],
83
+ '\n': ['\n', ws],
84
+ '\r': ['\r', ws],
85
+ '\t': ['\t', ws],
86
+ });
87
+ return json;
88
+ };
89
+ const _deterministic = () => {
90
+ const onenine = range('12');
91
+ const digit = {
92
+ '0': '0',
93
+ onenine,
94
+ };
95
+ const hex = {
96
+ digit,
97
+ AF: range('AF'),
98
+ af: range('af'),
99
+ };
100
+ const escape = {
101
+ ...set('"\\/bfnrt'),
102
+ u: ['u', hex, hex, hex, hex],
103
+ };
104
+ const character = {
105
+ 0: 0x20000021n,
106
+ 1: 0x2300005bn,
107
+ 2: 0x5d10ffffn,
108
+ escape: ['\\', escape],
109
+ };
110
+ const characters = repeat0(character);
111
+ const string = ['"', characters, '"'];
112
+ const digits0 = repeat0(digit);
113
+ const digits = [digit, digits0];
114
+ const integer = [option('-'), {
115
+ '0': '0',
116
+ onenine: [onenine, digits0],
117
+ }];
118
+ const fraction = option(['.', digits]);
119
+ const sign = option(set('+-'));
120
+ const exponent = option([set('Ee'), sign, digits]);
121
+ const number = [integer, fraction, exponent];
122
+ const ws = repeat0(set(' \n\r\t'));
123
+ const value = () => ({
124
+ object,
125
+ array,
126
+ string,
127
+ number,
128
+ true: 'true',
129
+ false: 'false',
130
+ null: 'null'
131
+ });
132
+ const commaJoin0 = ([open, close], a) => [open, ws, join0([a, ws], [',', ws]), close];
133
+ const array = commaJoin0('[]', value);
134
+ const member = [string, ws, ':', ws, value];
135
+ const object = commaJoin0('{}', member);
136
+ const json = [ws, value, ws];
137
+ return json;
138
+ };
package/fsc/bnf.f.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { type Rule } from '../bnf/func/module.f.ts';
2
+ export declare const wsModule: Rule;
package/fsc/bnf.f.js ADDED
@@ -0,0 +1,54 @@
1
+ import { cp, range, remove, set, str } from "../bnf/func/module.f.js";
2
+ import { digit, json, unicode, ws0, ws1, wsNoNewLine0 } from "./json.f.js";
3
+ export const wsModule = () => [[ws0, module]];
4
+ const module = () => [
5
+ [json, ws0],
6
+ [fjs],
7
+ ];
8
+ const fjs = () => [
9
+ [],
10
+ [...str('const'), ws1, id, ws0, cp('='), ws0, json, wsNoNewLine0, fjsTail],
11
+ [...str('export'), ws1, ...str('default'), ws1, json],
12
+ ];
13
+ const fjsTail = () => [
14
+ [],
15
+ [cp('\n'), ws0, fjs],
16
+ ];
17
+ // line comment
18
+ const lineItem = () => remove(unicode, [cp('\n')]);
19
+ const line = () => [
20
+ [],
21
+ [lineItem, line]
22
+ ];
23
+ const lineComment = () => [
24
+ [cp('/'), commentTail, cp('\n')]
25
+ ];
26
+ const multiLineSkip = () => remove(unicode, [cp('/')]);
27
+ const multiLineItem = () => remove(unicode, [cp('*')]);
28
+ const multiLine = () => [
29
+ [cp('*'), multiLineTail],
30
+ [multiLineItem, multiLine]
31
+ ];
32
+ const multiLineTail = () => [
33
+ [cp('/')],
34
+ [multiLineSkip, multiLine]
35
+ ];
36
+ const commentTail = () => [
37
+ [cp('/'), lineComment],
38
+ [cp('*'), multiLine],
39
+ ];
40
+ // id
41
+ const id = () => [[alpha, idTail0]];
42
+ const alpha = () => [
43
+ [range('AZ')],
44
+ [range('az')],
45
+ ...set('_$'),
46
+ ];
47
+ const idTail0 = () => [
48
+ [],
49
+ [alphaDigit, idTail0],
50
+ ];
51
+ const alphaDigit = () => [
52
+ [alpha],
53
+ [digit],
54
+ ];
@@ -0,0 +1,7 @@
1
+ import { type Rule, type TerminalRange } from '../bnf/func/module.f.ts';
2
+ export declare const wsNoNewLine0: Rule;
3
+ export declare const ws0: Rule;
4
+ export declare const ws1: Rule;
5
+ export declare const json: Rule;
6
+ export declare const unicode: TerminalRange;
7
+ export declare const digit: Rule;
package/fsc/json.f.js ADDED
@@ -0,0 +1,89 @@
1
+ import { cp, join0, range, remove, repeat0, set, str } from "../bnf/func/module.f.js";
2
+ // space
3
+ const wsNoNewLineItem = () => set(' \t\r');
4
+ export const wsNoNewLine0 = () => [
5
+ [],
6
+ [wsNoNewLineItem, wsNoNewLine0],
7
+ ];
8
+ const wsItem = () => [
9
+ [wsNoNewLineItem],
10
+ str('\n'),
11
+ ];
12
+ export const ws0 = () => [
13
+ [],
14
+ [ws1],
15
+ ];
16
+ export const ws1 = () => [
17
+ [wsItem, ws0]
18
+ ];
19
+ //
20
+ export const json = () => [
21
+ [object],
22
+ [array],
23
+ [number],
24
+ [string],
25
+ str('true'),
26
+ str('false'),
27
+ str('null'),
28
+ ];
29
+ //
30
+ const separator = () => [[cp(','), ws0]];
31
+ // object
32
+ const member = () => [[string, ws0, cp(':'), ws0, json, ws0]];
33
+ const object = () => [
34
+ [cp('{'), ws0, join0(member, separator), cp('}')]
35
+ ];
36
+ // array
37
+ const element = () => [
38
+ [json, ws0]
39
+ ];
40
+ const array = () => [
41
+ [cp('['), ws0, join0(element, separator), cp(']')]
42
+ ];
43
+ // string
44
+ const character = () => [
45
+ ...remove(unicode, [cp('"'), cp('\\')]),
46
+ [cp('\\'), escape],
47
+ ];
48
+ const string = () => [[cp('"'), repeat0(character), cp('"')]];
49
+ export const unicode = [0x20, 0x10FFFF];
50
+ const escape = () => [
51
+ ...set('"\\/bfnrt'),
52
+ [cp('u'), hex, hex, hex, hex] // 117
53
+ ];
54
+ const hex = () => [
55
+ [digit],
56
+ [range('AF')],
57
+ [range('af')],
58
+ ];
59
+ // number
60
+ const number = () => [
61
+ [uNumber],
62
+ [cp('-'), uNumber],
63
+ ];
64
+ const uNumber = () => [
65
+ [uint, fraction0, exponent0],
66
+ ];
67
+ const uint = () => [
68
+ str('0'),
69
+ [range('19'), digits0]
70
+ ];
71
+ export const digit = () => [[range('09')]];
72
+ const digits0 = repeat0(digit);
73
+ const digits1 = () => [
74
+ [digit, digits0]
75
+ ];
76
+ const fraction0 = () => [
77
+ [],
78
+ [cp('.'), digits1]
79
+ ];
80
+ const exponent0 = () => [
81
+ [],
82
+ [e, sign, digits1],
83
+ ];
84
+ const e = () => set('eE');
85
+ const sign = () => [
86
+ [],
87
+ [cp('+')],
88
+ [cp('-')],
89
+ ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "functionalscript",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "**/*.f.d.ts",
@@ -6,6 +6,7 @@
6
6
  export type Array1<T> = readonly [T];
7
7
  export type Index1 = 0;
8
8
  export type Array2<T> = readonly [T, T];
9
+ export declare const isArray2: <T>(a: readonly T[]) => a is Array2<T>;
9
10
  export type Tuple2<T0, T1> = readonly [T0, T1];
10
11
  export type Index2 = 0 | 1;
11
12
  export type Array3<T> = readonly [T, T, T];
@@ -4,6 +4,7 @@
4
4
  * @module
5
5
  */
6
6
  import { map } from "../nullable/module.f.js";
7
+ export const isArray2 = (a) => a.length === 2;
7
8
  const uncheckTail = (a) => a.slice(1);
8
9
  const uncheckHead = (a) => a.slice(0, -1);
9
10
  export const at = (i) => (a) => {
@@ -13,6 +13,7 @@
13
13
  * const logValue = log2(8n) // 3n
14
14
  * const bitCount = bitLength(255n) // 8n
15
15
  * const bitmask = mask(5n) // 31n
16
+ * const m = min(3n)(13n) // 3n
16
17
  * ```
17
18
  */
18
19
  import { type Sign } from '../function/compare/module.f.ts';
@@ -42,6 +43,7 @@ export declare const serialize: (a: bigint) => string;
42
43
  * of the most significant bit.
43
44
  * 2. **Binary Search Phase:** Refines the result by halving the step size and incrementally
44
45
  * determining the exact value of the logarithm.
46
+ * 3. **Remainder Phase:** Using `Math.log2`.
45
47
  */
46
48
  export declare const log2: (v: bigint) => bigint;
47
49
  /**
@@ -78,3 +80,7 @@ export declare const bitLength: (v: bigint) => bigint;
78
80
  * ```
79
81
  */
80
82
  export declare const mask: (len: bigint) => bigint;
83
+ /**
84
+ * A minimal value.
85
+ */
86
+ export declare const min: (a: bigint) => (b: bigint) => bigint;
@@ -13,6 +13,7 @@
13
13
  * const logValue = log2(8n) // 3n
14
14
  * const bitCount = bitLength(255n) // 8n
15
15
  * const bitmask = mask(5n) // 31n
16
+ * const m = min(3n)(13n) // 3n
16
17
  * ```
17
18
  */
18
19
  import { unsafeCmp } from "../function/compare/module.f.js";
@@ -39,13 +40,19 @@ export const serialize = (a) => `${a}n`;
39
40
  * of the most significant bit.
40
41
  * 2. **Binary Search Phase:** Refines the result by halving the step size and incrementally
41
42
  * determining the exact value of the logarithm.
43
+ * 3. **Remainder Phase:** Using `Math.log2`.
42
44
  */
43
45
  export const log2 = (v) => {
44
46
  if (v <= 0n) {
45
47
  return -1n;
46
48
  }
47
- let result = 31n;
48
- let i = 32n;
49
+ //
50
+ // 1. Fast Doubling.
51
+ //
52
+ let result = -1n;
53
+ // `bigints` higher than 2**1023 may lead to `Inf` during conversion to `number`.
54
+ // For example: `Number((1n << 1024n) - (1n << 970n)) === Inf`.
55
+ let i = 1023n;
49
56
  while (true) {
50
57
  const n = v >> i;
51
58
  if (n === 0n) {
@@ -56,9 +63,12 @@ export const log2 = (v) => {
56
63
  result += i;
57
64
  i <<= 1n;
58
65
  }
66
+ //
67
+ // 2. Binary Search.
68
+ //
59
69
  // We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0.
60
- // Because of this, We check if `i` is greater than 32 before we divide it by 2.
61
- while (i !== 32n) {
70
+ // Because of this, We check if `i` is greater than 1023 before we divide it by 2.
71
+ while (i !== 1023n) {
62
72
  i >>= 1n;
63
73
  const n = v >> i;
64
74
  if (n !== 0n) {
@@ -66,7 +76,15 @@ export const log2 = (v) => {
66
76
  v = n;
67
77
  }
68
78
  }
69
- return result - BigInt(Math.clz32(Number(v)));
79
+ //
80
+ // 3. Remainder Phase.
81
+ //
82
+ // We know that `v` is less than `1n << 1023` so we can calculate a remainder using
83
+ // `Math.log2`.
84
+ const rem = BigInt(Math.log2(Number(v)) | 0);
85
+ // (v >> rem) is either `0` or `1`, and it's used as a correction for
86
+ // Math.log2 rounding.
87
+ return result + rem + (v >> rem);
70
88
  };
71
89
  /**
72
90
  * Calculates the bit length of a given BigInt.
@@ -110,3 +128,7 @@ export const bitLength = (v) => {
110
128
  * ```
111
129
  */
112
130
  export const mask = (len) => (1n << len) - 1n;
131
+ /**
132
+ * A minimal value.
133
+ */
134
+ export const min = (a) => (b) => a < b ? a : b;
@@ -1,10 +1,13 @@
1
+ export declare const clz32Log2: (v: bigint) => bigint;
1
2
  declare const _default: {
2
3
  example: () => void;
3
- benchmark: {
4
- str: () => void;
5
- stringHexLog2: () => void;
6
- oldLog2: () => void;
7
- log2: () => void;
4
+ benchmark: () => {
5
+ big: {
6
+ [k: string]: () => void;
7
+ };
8
+ small: {
9
+ [k: string]: () => void;
10
+ };
8
11
  };
9
12
  mask: () => void;
10
13
  sum: () => void;
@@ -1,4 +1,4 @@
1
- import { sum, abs, serialize, log2, bitLength, mask } from "./module.f.js";
1
+ import { sum, abs, serialize, log2, bitLength, mask, min } from "./module.f.js";
2
2
  const oldLog2 = (v) => {
3
3
  if (v <= 0n) {
4
4
  return -1n;
@@ -27,24 +27,83 @@ const oldLog2 = (v) => {
27
27
  }
28
28
  return result;
29
29
  };
30
- const stringLog2 = (v) => BigInt(v.toString(2).length) - 1n;
31
- const stringHexLog2 = (v) => {
30
+ const strBinLog2 = (v) => BigInt(v.toString(2).length) - 1n;
31
+ const strHexLog2 = (v) => {
32
32
  const len = (BigInt(v.toString(16).length) - 1n) << 2n;
33
- const x = v >> len;
34
- return len + 31n - BigInt(Math.clz32(Number(x)));
33
+ return len + 31n - BigInt(Math.clz32(Number(v >> len)));
35
34
  };
36
- const benchmark = (f) => () => {
35
+ const str32Log2 = (v) => {
36
+ const len = (BigInt(v.toString(32).length) - 1n) * 5n;
37
+ return len + 31n - BigInt(Math.clz32(Number(v >> len)));
38
+ };
39
+ export const clz32Log2 = (v) => {
40
+ if (v <= 0n) {
41
+ return -1n;
42
+ }
43
+ let result = 31n;
44
+ let i = 32n;
45
+ while (true) {
46
+ const n = v >> i;
47
+ if (n === 0n) {
48
+ // overshot
49
+ break;
50
+ }
51
+ v = n;
52
+ result += i;
53
+ i <<= 1n;
54
+ }
55
+ // We know that `v` is not 0 so it doesn't make sense to check `n` when `i` is 0.
56
+ // Because of this, We check if `i` is greater than 32 before we divide it by 2.
57
+ while (i !== 32n) {
58
+ i >>= 1n;
59
+ const n = v >> i;
60
+ if (n !== 0n) {
61
+ result += i;
62
+ v = n;
63
+ }
64
+ }
65
+ return result - BigInt(Math.clz32(Number(v)));
66
+ };
67
+ const benchmark = f => () => {
37
68
  let e = 1048575n;
38
69
  let c = 1n << e;
39
- for (let i = 0n; i < 1_000; ++i) {
40
- const x = f(c);
41
- if (x !== e) {
42
- throw x;
70
+ for (let i = 0n; i < 1_100; ++i) {
71
+ {
72
+ const x = f(c);
73
+ if (x !== e) {
74
+ throw x;
75
+ }
76
+ }
77
+ {
78
+ const x = f(c - 1n);
79
+ if (x !== e - 1n) {
80
+ throw [e, x];
81
+ }
43
82
  }
44
83
  c >>= 1n;
45
84
  --e;
46
85
  }
47
86
  };
87
+ const benchmarkSmall = f => () => {
88
+ let e = 2000n;
89
+ let c = 1n << e;
90
+ do {
91
+ {
92
+ const x = f(c);
93
+ if (x !== e) {
94
+ throw [e, x];
95
+ }
96
+ }
97
+ for (let j = 1n; j < min(c >> 1n)(1000n); ++j) {
98
+ const x = f(c - j);
99
+ if (x !== e - 1n) {
100
+ throw [j, e, x];
101
+ }
102
+ }
103
+ c >>= 1n;
104
+ --e;
105
+ } while (c !== 0n);
106
+ };
48
107
  export default {
49
108
  example: () => {
50
109
  const total = sum([1n, 2n, 3n]); // 6n
@@ -53,26 +112,39 @@ export default {
53
112
  }
54
113
  const absoluteValue = abs(-42n); // 42n
55
114
  if (absoluteValue !== 42n) {
56
- throw total;
115
+ throw absoluteValue;
57
116
  }
58
117
  const logValue = log2(8n); // 3n
59
118
  if (logValue !== 3n) {
60
- throw total;
119
+ throw logValue;
61
120
  }
62
121
  const bitCount = bitLength(255n); // 8n
63
122
  if (bitCount !== 8n) {
64
- throw total;
123
+ throw bitCount;
65
124
  }
66
125
  const bitmask = mask(5n); // 31n
67
126
  if (bitmask !== 31n) {
68
- throw total;
127
+ throw benchmark;
128
+ }
129
+ const m = min(3n)(13n); // 3n
130
+ if (m !== 3n) {
131
+ throw m;
69
132
  }
70
133
  },
71
- benchmark: {
72
- str: benchmark(stringLog2),
73
- stringHexLog2: benchmark(stringHexLog2),
74
- oldLog2: benchmark(oldLog2),
75
- log2: benchmark(log2),
134
+ benchmark: () => {
135
+ const list = {
136
+ strBinLog2,
137
+ strHexLog2,
138
+ str32Log2,
139
+ oldLog2,
140
+ clz32Log2,
141
+ log2,
142
+ };
143
+ const transform = (b) => Object.fromEntries(Object.entries(list).map(([k, f]) => [k, b(f)]));
144
+ return {
145
+ big: transform(benchmark),
146
+ small: transform(benchmarkSmall),
147
+ };
76
148
  },
77
149
  mask: () => {
78
150
  const result = mask(3n); // 7n