functionalscript 0.3.12 → 0.3.14
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/module.f.d.ts +67 -1
- package/bnf/djs/module.f.js +202 -1
- package/bnf/djs/test.f.d.ts +8 -1
- package/bnf/djs/test.f.js +143 -82
- package/bnf/func/module.f.d.ts +141 -0
- package/bnf/func/module.f.js +111 -0
- package/bnf/func/test.f.d.ts +12 -0
- package/bnf/func/test.f.js +230 -0
- package/bnf/func/testlib.f.d.ts +25 -0
- package/bnf/func/testlib.f.js +150 -0
- package/nanvm-lib/tests/test.f.d.ts +33 -0
- package/nanvm-lib/tests/test.f.js +45 -6
- package/package.json +9 -7
- package/text/utf8/module.f.d.ts +31 -2
- package/text/utf8/module.f.js +116 -28
- package/types/array/module.f.d.ts +19 -5
- package/types/array/module.f.js +13 -0
- package/types/array/test.f.d.ts +1 -0
- package/types/array/test.f.js +11 -2
- package/types/bit_vec/module.f.d.ts +2 -2
- package/types/bit_vec/module.f.js +16 -4
- package/bnf/module.f.d.ts +0 -135
- package/bnf/module.f.js +0 -142
- package/bnf/test.f.d.ts +0 -65
- package/bnf/test.f.js +0 -368
package/bnf/djs/module.f.d.ts
CHANGED
|
@@ -2,10 +2,76 @@
|
|
|
2
2
|
* Rules for serializing and deserializing the BNF grammar.
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Define a simple grammar
|
|
10
|
+
* const grammar: Rule = () => [
|
|
11
|
+
* [range('AZ')], // 'A-Z'
|
|
12
|
+
* [range('az'), grammar], // 'a-z' followed by more grammar
|
|
13
|
+
* ]
|
|
14
|
+
*
|
|
15
|
+
* const parse = parser(toRuleMap(grammar))
|
|
16
|
+
*
|
|
17
|
+
* // Parse an input
|
|
18
|
+
* const input = toArray(stringToCodePointList('abcdefgA'))
|
|
19
|
+
* const result = parse(grammar.name, input)
|
|
20
|
+
* if (result === null) { throw result }
|
|
21
|
+
* const [, b] = result
|
|
22
|
+
* if (b?.length !== 0) { throw b }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import type { CodePoint } from '../../text/utf16/module.f.ts';
|
|
26
|
+
import type { TerminalRange, Rule as FRule } from '../func/module.f.ts';
|
|
27
|
+
/**
|
|
28
|
+
* Represents a sequence of grammar rules, including terminal ranges or rule identifiers.
|
|
29
|
+
* @template Id - The type for rule identifiers.
|
|
5
30
|
*/
|
|
6
|
-
import type { TerminalRange } from '../module.f.ts';
|
|
7
31
|
export type Sequence<Id> = readonly (TerminalRange | Id)[];
|
|
32
|
+
/**
|
|
33
|
+
* Represents a grammar rule as a collection of sequences.
|
|
34
|
+
* @template Id - The type for rule identifiers.
|
|
35
|
+
*/
|
|
8
36
|
export type Rule<Id> = readonly Sequence<Id>[];
|
|
37
|
+
/**
|
|
38
|
+
* Represents a map of grammar rules, where each rule is identified by a unique string identifier.
|
|
39
|
+
* @template Id - The type for rule identifiers.
|
|
40
|
+
*/
|
|
9
41
|
export type RuleMap<Id extends string> = {
|
|
10
42
|
readonly [k in Id]: Rule<Id>;
|
|
11
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Transforming functional rule to a serializable rule map.
|
|
46
|
+
*
|
|
47
|
+
* @param src a functional rule.
|
|
48
|
+
* @returns a serializable rule map.
|
|
49
|
+
*/
|
|
50
|
+
export declare const toRuleMap: (src: FRule) => RuleMap<string>;
|
|
51
|
+
/**
|
|
52
|
+
* Represents a parsed Abstract Syntax Tree (AST) sequence.
|
|
53
|
+
*/
|
|
54
|
+
export type AstSequence = readonly (AstRule | CodePoint)[];
|
|
55
|
+
/**
|
|
56
|
+
* Represents a parsed AST rule, consisting of a rule name and its parsed sequence.
|
|
57
|
+
*/
|
|
58
|
+
export type AstRule = readonly [string, AstSequence];
|
|
59
|
+
/**
|
|
60
|
+
* Represents the remaining input after a match attempt, or `null` if no match is possible.
|
|
61
|
+
*/
|
|
62
|
+
export type Remainder = readonly CodePoint[] | null;
|
|
63
|
+
/**
|
|
64
|
+
* Represents the result of a match operation, including the parsed AST rule and the remainder of the input.
|
|
65
|
+
*/
|
|
66
|
+
export type MatchResult = readonly [AstRule, Remainder];
|
|
67
|
+
/**
|
|
68
|
+
* Represents an LL(1) parser function for matching input against grammar rules.
|
|
69
|
+
*/
|
|
70
|
+
export type Match = (name: string, s: readonly CodePoint[]) => MatchResult;
|
|
71
|
+
/**
|
|
72
|
+
* Creates a simple LL1 parser for the given map.
|
|
73
|
+
*
|
|
74
|
+
* @param map a prepared rule map.
|
|
75
|
+
* @returns a parser.
|
|
76
|
+
*/
|
|
77
|
+
export declare const parser: (rm: RuleMap<string>) => Match;
|
package/bnf/djs/module.f.js
CHANGED
|
@@ -2,5 +2,206 @@
|
|
|
2
2
|
* Rules for serializing and deserializing the BNF grammar.
|
|
3
3
|
*
|
|
4
4
|
* @module
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
*
|
|
8
|
+
* ```ts
|
|
9
|
+
* // Define a simple grammar
|
|
10
|
+
* const grammar: Rule = () => [
|
|
11
|
+
* [range('AZ')], // 'A-Z'
|
|
12
|
+
* [range('az'), grammar], // 'a-z' followed by more grammar
|
|
13
|
+
* ]
|
|
14
|
+
*
|
|
15
|
+
* const parse = parser(toRuleMap(grammar))
|
|
16
|
+
*
|
|
17
|
+
* // Parse an input
|
|
18
|
+
* const input = toArray(stringToCodePointList('abcdefgA'))
|
|
19
|
+
* const result = parse(grammar.name, input)
|
|
20
|
+
* if (result === null) { throw result }
|
|
21
|
+
* const [, b] = result
|
|
22
|
+
* if (b?.length !== 0) { throw b }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
import { empty as emptyArray } from "../../types/array/module.f.js";
|
|
26
|
+
import { strictEqual } from "../../types/function/operator/module.f.js";
|
|
27
|
+
import { toArray } from "../../types/list/module.f.js";
|
|
28
|
+
import { contains } from "../../types/range/module.f.js";
|
|
29
|
+
import { rangeMap } from "../../types/range_map/module.f.js";
|
|
30
|
+
const findName = (map, rule) => {
|
|
31
|
+
const { name } = rule;
|
|
32
|
+
let result = name;
|
|
33
|
+
{
|
|
34
|
+
let i = 0;
|
|
35
|
+
while (result in map) {
|
|
36
|
+
result = name + i;
|
|
37
|
+
++i;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return result;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Add a new rule to the temporary map.
|
|
44
|
+
*/
|
|
45
|
+
const tmpAdd = ({ queue, result }) => (src) => {
|
|
46
|
+
// find a name for the item.
|
|
47
|
+
const name = findName(result, src);
|
|
48
|
+
return [{
|
|
49
|
+
// add the item to a queue under the name.
|
|
50
|
+
queue: [...queue, [src, name]],
|
|
51
|
+
// add the name to the result and fill the rule later.
|
|
52
|
+
result: { ...result, [name]: emptyArray },
|
|
53
|
+
}, name];
|
|
54
|
+
};
|
|
55
|
+
const tmpNew = tmpAdd({
|
|
56
|
+
queue: emptyArray,
|
|
57
|
+
result: {},
|
|
58
|
+
});
|
|
59
|
+
const tmpItem = (tmp, src) => {
|
|
60
|
+
const found = tmp.queue.find(([f]) => f === src);
|
|
61
|
+
return found !== undefined ? [tmp, found[1]] : tmpAdd(tmp)(src);
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Transforming functional rule to a serializable rule map.
|
|
65
|
+
*
|
|
66
|
+
* @param src a functional rule.
|
|
67
|
+
* @returns a serializable rule map.
|
|
68
|
+
*/
|
|
69
|
+
export const toRuleMap = (src) => {
|
|
70
|
+
let [tmp] = tmpNew(src);
|
|
71
|
+
let i = 0;
|
|
72
|
+
do {
|
|
73
|
+
const [srcOr, name] = tmp.queue[i];
|
|
74
|
+
let rule = emptyArray;
|
|
75
|
+
// iterate all sequences of the `Or` rule.
|
|
76
|
+
for (const srcSeq of srcOr()) {
|
|
77
|
+
let s = emptyArray;
|
|
78
|
+
// iterate all items of the sequence.
|
|
79
|
+
for (const srcItem of srcSeq) {
|
|
80
|
+
let item;
|
|
81
|
+
if (srcItem instanceof Array) {
|
|
82
|
+
item = srcItem;
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
[tmp, item] = tmpItem(tmp, srcItem);
|
|
86
|
+
}
|
|
87
|
+
s = [...s, item];
|
|
88
|
+
}
|
|
89
|
+
rule = [...rule, s];
|
|
90
|
+
}
|
|
91
|
+
// fix the rule in the result.
|
|
92
|
+
tmp = {
|
|
93
|
+
queue: tmp.queue,
|
|
94
|
+
result: { ...tmp.result, [name]: rule },
|
|
95
|
+
};
|
|
96
|
+
++i;
|
|
97
|
+
} while (i !== tmp.queue.length);
|
|
98
|
+
return tmp.result;
|
|
99
|
+
};
|
|
100
|
+
const dispatchOp = rangeMap({
|
|
101
|
+
union: a => b => {
|
|
102
|
+
if (a === null) {
|
|
103
|
+
return b;
|
|
104
|
+
}
|
|
105
|
+
if (b === null) {
|
|
106
|
+
return a;
|
|
107
|
+
}
|
|
108
|
+
throw ['can not merge [', a, '][', b, ']'];
|
|
109
|
+
},
|
|
110
|
+
equal: strictEqual,
|
|
111
|
+
def: null,
|
|
112
|
+
});
|
|
113
|
+
/**
|
|
114
|
+
* Creates a dispatch map for LL1 parser.
|
|
115
|
+
*
|
|
116
|
+
* @param ruleMap a serializable rule map.
|
|
117
|
+
* @returns A dispatch map
|
|
118
|
+
*/
|
|
119
|
+
const dispatchMap = (ruleMap) => {
|
|
120
|
+
const dispatchSequence = (dm, sequence) => {
|
|
121
|
+
let empty = true;
|
|
122
|
+
let result = [];
|
|
123
|
+
for (const item of sequence) {
|
|
124
|
+
if (typeof item === 'string') {
|
|
125
|
+
dm = dispatchRule(dm, item);
|
|
126
|
+
const [e, dispatch] = dm[item];
|
|
127
|
+
result = toArray(dispatchOp.merge(result)(dispatch.map(x => [x[0] === null ? null : sequence, x[1]])));
|
|
128
|
+
if (e) {
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const dispatch = dispatchOp.fromRange(item)(sequence);
|
|
134
|
+
result = toArray(dispatchOp.merge(result)(dispatch));
|
|
135
|
+
}
|
|
136
|
+
empty = false;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
return [dm, [empty, result]];
|
|
140
|
+
};
|
|
141
|
+
const dispatchRule = (dm, name) => {
|
|
142
|
+
if (name in dm) {
|
|
143
|
+
return dm;
|
|
144
|
+
}
|
|
145
|
+
let empty = false;
|
|
146
|
+
let dispatch = [];
|
|
147
|
+
for (const sequence of ruleMap[name]) {
|
|
148
|
+
const [newDm, [e, d]] = dispatchSequence(dm, sequence);
|
|
149
|
+
dm = newDm;
|
|
150
|
+
empty ||= e;
|
|
151
|
+
dispatch = toArray(dispatchOp.merge(dispatch)(d));
|
|
152
|
+
}
|
|
153
|
+
return { ...dm, [name]: [empty, dispatch] };
|
|
154
|
+
};
|
|
155
|
+
let result = {};
|
|
156
|
+
for (const k in ruleMap) {
|
|
157
|
+
result = dispatchRule(result, k);
|
|
158
|
+
}
|
|
159
|
+
// TODO: validate all sequences if they are deterministic
|
|
160
|
+
return result;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Creates a simple LL1 parser for the given map.
|
|
164
|
+
*
|
|
165
|
+
* @param map a prepared rule map.
|
|
166
|
+
* @returns a parser.
|
|
5
167
|
*/
|
|
6
|
-
export {
|
|
168
|
+
export const parser = (rm) => {
|
|
169
|
+
const map = dispatchMap(rm);
|
|
170
|
+
const f = (name, s) => {
|
|
171
|
+
const mr = (a, r) => [[name, a], r];
|
|
172
|
+
const mre = (a) => mr(a, null);
|
|
173
|
+
const [empty, sequence] = map[name];
|
|
174
|
+
if (s.length === 0) {
|
|
175
|
+
return mr([], empty ? s : null);
|
|
176
|
+
}
|
|
177
|
+
const cp = s[0];
|
|
178
|
+
const i = dispatchOp.get(cp)(sequence);
|
|
179
|
+
if (i === null) {
|
|
180
|
+
return mr([], empty ? s : null);
|
|
181
|
+
}
|
|
182
|
+
let a = [];
|
|
183
|
+
let si = s;
|
|
184
|
+
for (const c of i) {
|
|
185
|
+
if (typeof c === 'string') {
|
|
186
|
+
// c is a name
|
|
187
|
+
const [astRule, newSi] = f(c, si);
|
|
188
|
+
a = [...a, astRule];
|
|
189
|
+
if (newSi === null) {
|
|
190
|
+
return mre(a);
|
|
191
|
+
}
|
|
192
|
+
si = newSi;
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
// c is TerminalRange
|
|
196
|
+
const [first, ...newSi] = si;
|
|
197
|
+
if (first === undefined || !contains(c)(first)) {
|
|
198
|
+
return mre(a);
|
|
199
|
+
}
|
|
200
|
+
a = [...a, first];
|
|
201
|
+
si = newSi;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return mr(a, si);
|
|
205
|
+
};
|
|
206
|
+
return f;
|
|
207
|
+
};
|
package/bnf/djs/test.f.d.ts
CHANGED
package/bnf/djs/test.f.js
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { cp, range, remove, str } from "../func/module.f.js";
|
|
2
|
+
import { parser, toRuleMap } from "./module.f.js";
|
|
3
|
+
import { classic } from "../func/testlib.f.js";
|
|
4
|
+
import * as j from "../../json/module.f.js";
|
|
5
|
+
import { sort } from "../../types/object/module.f.js";
|
|
6
|
+
import { stringToCodePointList } from "../../text/utf16/module.f.js";
|
|
7
|
+
import { toArray } from "../../types/list/module.f.js";
|
|
8
|
+
const stringify = j.stringify(sort);
|
|
9
|
+
const classicTest = () => {
|
|
6
10
|
const map = {
|
|
7
11
|
json: [
|
|
8
12
|
['element']
|
|
@@ -12,60 +16,58 @@ const classic = () => {
|
|
|
12
16
|
['array'],
|
|
13
17
|
['string'],
|
|
14
18
|
['number'],
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
str('true'),
|
|
20
|
+
str('false'),
|
|
21
|
+
str('null'),
|
|
18
22
|
],
|
|
19
23
|
object: [
|
|
20
|
-
[
|
|
21
|
-
[
|
|
24
|
+
[cp('{'), 'ws', cp('}')],
|
|
25
|
+
[cp('{'), 'members', cp('}')],
|
|
22
26
|
],
|
|
23
27
|
members: [
|
|
24
28
|
['member'],
|
|
25
|
-
['member',
|
|
29
|
+
['member', cp(','), 'members'],
|
|
26
30
|
],
|
|
27
31
|
member: [
|
|
28
|
-
['ws', 'string', 'ws',
|
|
32
|
+
['ws', 'string', 'ws', cp(':'), 'element'],
|
|
29
33
|
],
|
|
30
34
|
array: [
|
|
31
|
-
[
|
|
32
|
-
[
|
|
35
|
+
[cp('['), 'ws', cp(']')],
|
|
36
|
+
[cp('['), 'elements', cp(']')],
|
|
33
37
|
],
|
|
34
38
|
elements: [
|
|
35
39
|
['element'],
|
|
36
|
-
['element',
|
|
40
|
+
['element', cp(','), 'elements'],
|
|
37
41
|
],
|
|
38
42
|
element: [
|
|
39
43
|
['ws', 'value', 'ws'],
|
|
40
44
|
],
|
|
41
45
|
string: [
|
|
42
|
-
[
|
|
46
|
+
[cp('"'), 'characters', cp('"')],
|
|
43
47
|
],
|
|
44
48
|
characters: [
|
|
45
49
|
[],
|
|
46
50
|
['character', 'characters'],
|
|
47
51
|
],
|
|
48
52
|
character: [
|
|
49
|
-
[
|
|
50
|
-
[
|
|
51
|
-
[[0x5D, 0x10FFFF]], // 93-1114111
|
|
52
|
-
[c('\\'), 'escape'], // 92
|
|
53
|
+
...remove([0x20, 0x10FFFF], [cp('"'), cp('\\')]),
|
|
54
|
+
[cp('\\'), 'escape'], // 92
|
|
53
55
|
],
|
|
54
56
|
escape: [
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
[
|
|
57
|
+
str('"'),
|
|
58
|
+
str('\\'),
|
|
59
|
+
str('/'),
|
|
60
|
+
str('b'),
|
|
61
|
+
str('f'),
|
|
62
|
+
str('n'),
|
|
63
|
+
str('r'),
|
|
64
|
+
str('t'),
|
|
65
|
+
[cp('u'), 'hex', 'hex', 'hex', 'hex'],
|
|
64
66
|
],
|
|
65
67
|
hex: [
|
|
66
68
|
['digit'],
|
|
67
|
-
[
|
|
68
|
-
[
|
|
69
|
+
[range('AF')], // A-F
|
|
70
|
+
[range('af')], // a-f
|
|
69
71
|
],
|
|
70
72
|
number: [
|
|
71
73
|
['integer', 'fraction', 'exponent'],
|
|
@@ -73,43 +75,44 @@ const classic = () => {
|
|
|
73
75
|
integer: [
|
|
74
76
|
['digit'],
|
|
75
77
|
['onenine', 'digits'],
|
|
76
|
-
[
|
|
77
|
-
[
|
|
78
|
+
[cp('-'), 'digit'],
|
|
79
|
+
[cp('-'), 'onenine', 'digits'],
|
|
78
80
|
],
|
|
79
81
|
digits: [
|
|
80
82
|
['digit'],
|
|
81
83
|
['digit', 'digits'],
|
|
82
84
|
],
|
|
83
85
|
digit: [
|
|
84
|
-
[
|
|
86
|
+
[cp('0')],
|
|
85
87
|
['onenine'],
|
|
86
88
|
],
|
|
87
89
|
onenine: [
|
|
88
|
-
[
|
|
90
|
+
[range('19')],
|
|
89
91
|
],
|
|
90
92
|
fraction: [
|
|
91
93
|
[],
|
|
92
|
-
[
|
|
94
|
+
[cp('.'), 'digits'],
|
|
93
95
|
],
|
|
94
96
|
exponent: [
|
|
95
97
|
[],
|
|
96
|
-
[
|
|
97
|
-
[
|
|
98
|
+
[cp('E'), 'sign', 'digits'],
|
|
99
|
+
[cp('e'), 'sign', 'digits'],
|
|
98
100
|
],
|
|
99
101
|
sign: [
|
|
100
102
|
[],
|
|
101
|
-
[
|
|
102
|
-
[
|
|
103
|
+
[cp('+')],
|
|
104
|
+
[cp('-')],
|
|
103
105
|
],
|
|
104
106
|
ws: [
|
|
105
107
|
[],
|
|
106
|
-
[
|
|
107
|
-
[
|
|
108
|
-
[
|
|
109
|
-
[
|
|
108
|
+
[cp(' '), 'ws'],
|
|
109
|
+
[cp('\n'), 'ws'],
|
|
110
|
+
[cp('\r'), 'ws'],
|
|
111
|
+
[cp('\t'), 'ws'],
|
|
110
112
|
],
|
|
111
113
|
};
|
|
112
|
-
const
|
|
114
|
+
const result = map;
|
|
115
|
+
return result;
|
|
113
116
|
};
|
|
114
117
|
const deterministic = () => {
|
|
115
118
|
const map = {
|
|
@@ -117,13 +120,13 @@ const deterministic = () => {
|
|
|
117
120
|
['ws', 'element']
|
|
118
121
|
],
|
|
119
122
|
value: [
|
|
120
|
-
[
|
|
121
|
-
[
|
|
123
|
+
[cp('{'), 'ws', 'object', cp('}')],
|
|
124
|
+
[cp('['), 'ws', 'array', cp(']')],
|
|
122
125
|
['string'],
|
|
123
126
|
['number'],
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
+
str('true'),
|
|
128
|
+
str('false'),
|
|
129
|
+
str('null'),
|
|
127
130
|
],
|
|
128
131
|
object: [
|
|
129
132
|
[],
|
|
@@ -131,10 +134,10 @@ const deterministic = () => {
|
|
|
131
134
|
],
|
|
132
135
|
members: [
|
|
133
136
|
[],
|
|
134
|
-
[
|
|
137
|
+
[cp(','), 'ws', 'member', 'members'],
|
|
135
138
|
],
|
|
136
139
|
member: [
|
|
137
|
-
['string', 'ws',
|
|
140
|
+
['string', 'ws', cp(':'), 'ws', 'element'],
|
|
138
141
|
],
|
|
139
142
|
array: [
|
|
140
143
|
[],
|
|
@@ -142,46 +145,44 @@ const deterministic = () => {
|
|
|
142
145
|
],
|
|
143
146
|
elements: [
|
|
144
147
|
[],
|
|
145
|
-
[
|
|
148
|
+
[cp(','), 'ws', 'element', 'elements'],
|
|
146
149
|
],
|
|
147
150
|
element: [
|
|
148
151
|
['value', 'ws'],
|
|
149
152
|
],
|
|
150
153
|
string: [
|
|
151
|
-
[
|
|
154
|
+
[cp('"'), 'characters', cp('"')],
|
|
152
155
|
],
|
|
153
156
|
characters: [
|
|
154
157
|
[],
|
|
155
158
|
['character', 'characters'],
|
|
156
159
|
],
|
|
157
160
|
character: [
|
|
158
|
-
[
|
|
159
|
-
[
|
|
160
|
-
[[0x5D, 0x10FFFF]], // 93-1114111
|
|
161
|
-
[c('\\'), 'escape'], // 92
|
|
161
|
+
...remove([0x20, 0x10FFFF], [cp('"'), cp('\\')]),
|
|
162
|
+
[cp('\\'), 'escape'], // 92
|
|
162
163
|
],
|
|
163
164
|
escape: [
|
|
164
|
-
[
|
|
165
|
-
[
|
|
166
|
-
[
|
|
167
|
-
[
|
|
168
|
-
[
|
|
169
|
-
[
|
|
170
|
-
[
|
|
171
|
-
[
|
|
172
|
-
[
|
|
165
|
+
[cp('"')],
|
|
166
|
+
[cp('\\')],
|
|
167
|
+
[cp('/')],
|
|
168
|
+
[cp('b')],
|
|
169
|
+
[cp('f')],
|
|
170
|
+
[cp('n')],
|
|
171
|
+
[cp('r')],
|
|
172
|
+
[cp('t')],
|
|
173
|
+
[cp('u'), 'hex', 'hex', 'hex', 'hex'],
|
|
173
174
|
],
|
|
174
175
|
hex: [
|
|
175
176
|
['digit'],
|
|
176
|
-
[
|
|
177
|
-
[
|
|
177
|
+
[range('AF')],
|
|
178
|
+
[range('af')],
|
|
178
179
|
],
|
|
179
180
|
number: [
|
|
180
181
|
['integer', 'fraction', 'exponent'],
|
|
181
|
-
[
|
|
182
|
+
[cp('-'), 'integer', 'fraction', 'exponent'],
|
|
182
183
|
],
|
|
183
184
|
integer: [
|
|
184
|
-
[
|
|
185
|
+
[cp('0')],
|
|
185
186
|
['onenine', 'digits'],
|
|
186
187
|
],
|
|
187
188
|
digits: [
|
|
@@ -189,33 +190,93 @@ const deterministic = () => {
|
|
|
189
190
|
['digit', 'digits'],
|
|
190
191
|
],
|
|
191
192
|
digit: [
|
|
192
|
-
[
|
|
193
|
+
[cp('0')],
|
|
193
194
|
['onenine'],
|
|
194
195
|
],
|
|
195
196
|
onenine: [
|
|
196
|
-
[
|
|
197
|
+
[range('19')],
|
|
197
198
|
],
|
|
198
199
|
fraction: [
|
|
199
200
|
[],
|
|
200
|
-
[
|
|
201
|
+
[cp('.'), 'digit', 'digits'],
|
|
201
202
|
],
|
|
202
203
|
exponent: [
|
|
203
204
|
[],
|
|
204
|
-
[
|
|
205
|
-
[
|
|
205
|
+
[cp('E'), 'sign', 'digit', 'digits'],
|
|
206
|
+
[cp('e'), 'sign', 'digit', 'digits'],
|
|
206
207
|
],
|
|
207
208
|
sign: [
|
|
208
209
|
[],
|
|
209
|
-
[
|
|
210
|
-
[
|
|
210
|
+
[cp('+')],
|
|
211
|
+
[cp('-')],
|
|
211
212
|
],
|
|
212
213
|
ws: [
|
|
213
214
|
[],
|
|
214
|
-
[
|
|
215
|
-
[
|
|
216
|
-
[
|
|
217
|
-
[
|
|
215
|
+
[cp(' '), 'ws'],
|
|
216
|
+
[cp('\n'), 'ws'],
|
|
217
|
+
[cp('\r'), 'ws'],
|
|
218
|
+
[cp('\t'), 'ws'],
|
|
218
219
|
],
|
|
219
220
|
};
|
|
220
221
|
const _map = map;
|
|
222
|
+
return _map;
|
|
223
|
+
};
|
|
224
|
+
export default {
|
|
225
|
+
example: {
|
|
226
|
+
module: () => {
|
|
227
|
+
// Define a simple grammar
|
|
228
|
+
const grammar = () => [
|
|
229
|
+
[range('AZ')], // 'A-Z'
|
|
230
|
+
[range('az'), grammar], // 'a-z' followed by more grammar
|
|
231
|
+
];
|
|
232
|
+
const ruleMap = toRuleMap(grammar);
|
|
233
|
+
const parse = parser(ruleMap);
|
|
234
|
+
// Parse an input
|
|
235
|
+
const input = toArray(stringToCodePointList('abcdefgA'));
|
|
236
|
+
const result = parse(grammar.name, input);
|
|
237
|
+
if (result === null) {
|
|
238
|
+
throw result;
|
|
239
|
+
}
|
|
240
|
+
const [, b] = result;
|
|
241
|
+
if (b?.length !== 0) {
|
|
242
|
+
throw b;
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
classic: () => {
|
|
247
|
+
const c = classic();
|
|
248
|
+
const json = stringify(toRuleMap(c.json));
|
|
249
|
+
const jsonE = stringify(classicTest());
|
|
250
|
+
if (json !== jsonE) {
|
|
251
|
+
//console.error(json)
|
|
252
|
+
//console.error(jsonE)
|
|
253
|
+
throw [json, jsonE];
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
map: () => {
|
|
257
|
+
const f = parser(deterministic());
|
|
258
|
+
// console.error(stringify(x))
|
|
259
|
+
//
|
|
260
|
+
const isSuccess = (s) => s?.length === 0;
|
|
261
|
+
const expect = (s, success) => {
|
|
262
|
+
const [a, r] = f('json', toArray(stringToCodePointList(s)));
|
|
263
|
+
if (isSuccess(r) !== success) {
|
|
264
|
+
throw r;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
//
|
|
268
|
+
expect(' true ', true);
|
|
269
|
+
expect(' tr2ue ', false);
|
|
270
|
+
expect(' true" ', false);
|
|
271
|
+
expect(' "Hello" ', true);
|
|
272
|
+
expect(' "Hello ', false);
|
|
273
|
+
expect(' "Hello\\n\\r\\"" ', true);
|
|
274
|
+
expect(' -56.7e+5 ', true);
|
|
275
|
+
expect(' h-56.7e+5 ', false);
|
|
276
|
+
expect(' -56.7e+5 3', false);
|
|
277
|
+
expect(' [ 12, false, "a"] ', true);
|
|
278
|
+
expect(' [ 12, false2, "a"] ', false);
|
|
279
|
+
expect(' { "q": [ 12, false, [{}], "a"] } ', true);
|
|
280
|
+
expect(' { "q": [ 12, false, [}], "a"] } ', false);
|
|
281
|
+
}
|
|
221
282
|
};
|