functionalscript 0.7.0 → 0.8.0
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/LICENSE +21 -661
- package/README.md +3 -2
- package/bnf/data/module.f.d.ts +10 -6
- package/bnf/data/module.f.js +62 -23
- package/bnf/data/test.f.d.ts +3 -0
- package/bnf/data/test.f.js +323 -14
- package/bnf/module.f.d.ts +5 -4
- package/bnf/module.f.js +1 -1
- package/bnf/testlib.f.js +1 -1
- package/fsc/test.f.d.ts +5 -0
- package/fsc/test.f.js +65 -1
- package/nanvm-lib/tests/vm/test.f.d.ts +25 -0
- package/nanvm-lib/tests/vm/test.f.js +105 -0
- package/package.json +6 -6
- package/bnf/djs/module.f.d.ts +0 -77
- package/bnf/djs/module.f.js +0 -207
- package/bnf/djs/test.f.d.ts +0 -8
- package/bnf/djs/test.f.js +0 -277
package/bnf/testlib.f.js
CHANGED
|
@@ -130,8 +130,8 @@ export const deterministic = () => {
|
|
|
130
130
|
close,
|
|
131
131
|
];
|
|
132
132
|
const value = () => ({
|
|
133
|
-
object: commaJoin0Plus('{}', [string, ws, ':', ws, value]),
|
|
134
133
|
array: commaJoin0Plus('[]', value),
|
|
134
|
+
object: commaJoin0Plus('{}', [string, ws, ':', ws, value]),
|
|
135
135
|
string,
|
|
136
136
|
number,
|
|
137
137
|
true: 'true',
|
package/fsc/test.f.d.ts
CHANGED
package/fsc/test.f.js
CHANGED
|
@@ -8,11 +8,75 @@ const f = v => {
|
|
|
8
8
|
const n = one(v);
|
|
9
9
|
return s(_.init(n)[0]);
|
|
10
10
|
};
|
|
11
|
+
// this doesn't change a name of the function
|
|
12
|
+
const fn = (f, name) => ({ [name]: f }[name]);
|
|
13
|
+
const withName = (name) =>
|
|
14
|
+
// translated into one command: define a `function [name]() { return undefined }`
|
|
15
|
+
Object.getOwnPropertyDescriptor({ [name]: () => undefined }, name).value;
|
|
11
16
|
export default {
|
|
12
17
|
a: () => {
|
|
13
18
|
const x = f('1');
|
|
14
|
-
if (x
|
|
19
|
+
if (x !== '["1"]') {
|
|
15
20
|
throw x;
|
|
16
21
|
}
|
|
22
|
+
},
|
|
23
|
+
fn: () => {
|
|
24
|
+
const o = {
|
|
25
|
+
["hello world!"]: () => undefined
|
|
26
|
+
};
|
|
27
|
+
const f = o["hello world!"];
|
|
28
|
+
const { name } = f;
|
|
29
|
+
if (name !== "hello world!") {
|
|
30
|
+
throw name;
|
|
31
|
+
}
|
|
32
|
+
//
|
|
33
|
+
const f1 = { ["boring"]: () => undefined }["boring"];
|
|
34
|
+
if (f1.name !== "boring") {
|
|
35
|
+
throw f1.name;
|
|
36
|
+
}
|
|
37
|
+
//
|
|
38
|
+
const x = fn(() => undefined, "hello").name;
|
|
39
|
+
if (x !== "") {
|
|
40
|
+
throw x;
|
|
41
|
+
}
|
|
42
|
+
//
|
|
43
|
+
const m = withName("boring2").name;
|
|
44
|
+
if (m !== "boring2") {
|
|
45
|
+
throw m;
|
|
46
|
+
}
|
|
47
|
+
//
|
|
48
|
+
const a = function x() { return undefined; };
|
|
49
|
+
if (a.name !== "x") {
|
|
50
|
+
throw a.name;
|
|
51
|
+
}
|
|
52
|
+
},
|
|
53
|
+
//
|
|
54
|
+
f1: () => {
|
|
55
|
+
const m1 = () => undefined;
|
|
56
|
+
if (m1.name !== "m1") {
|
|
57
|
+
throw m1.name;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
//
|
|
61
|
+
f2: () => {
|
|
62
|
+
const m11 = (() => undefined);
|
|
63
|
+
if (m11.name !== "m11") {
|
|
64
|
+
throw m11.name;
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
//
|
|
68
|
+
f3: () => {
|
|
69
|
+
const m2 = true ? () => undefined : () => undefined;
|
|
70
|
+
// for `bun` it is `m2`:
|
|
71
|
+
// if (m2.name !== "") { throw m2.name }
|
|
72
|
+
// see also https://github.com/oven-sh/bun/issues/20398
|
|
73
|
+
},
|
|
74
|
+
f4: () => {
|
|
75
|
+
const id = (i) => i;
|
|
76
|
+
const f = id(() => undefined);
|
|
77
|
+
// for `bun` it is `m2`:
|
|
78
|
+
if (f.name !== "") {
|
|
79
|
+
throw f.name;
|
|
80
|
+
}
|
|
17
81
|
}
|
|
18
82
|
};
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
stringCoercion: {
|
|
3
|
+
number: () => void;
|
|
4
|
+
bool: () => void;
|
|
5
|
+
null: () => void;
|
|
6
|
+
undefined: () => void;
|
|
7
|
+
bigint: () => void;
|
|
8
|
+
array: () => void;
|
|
9
|
+
func: () => void;
|
|
10
|
+
object: {
|
|
11
|
+
norm: () => void;
|
|
12
|
+
toString: () => void;
|
|
13
|
+
toStringThrow: {
|
|
14
|
+
throw: () => void;
|
|
15
|
+
};
|
|
16
|
+
toStringNotFunc: {
|
|
17
|
+
throw: () => void;
|
|
18
|
+
};
|
|
19
|
+
toStringNonPrimitive: {
|
|
20
|
+
throw: () => void;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export default _default;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
const stringCoercion = (a) => a + '';
|
|
2
|
+
export default {
|
|
3
|
+
stringCoercion: {
|
|
4
|
+
number: () => {
|
|
5
|
+
if (stringCoercion(123) !== '123') {
|
|
6
|
+
throw [123, 'toString', '123'];
|
|
7
|
+
}
|
|
8
|
+
if (stringCoercion(-456) !== '-456') {
|
|
9
|
+
throw [-456, 'toString', '-456'];
|
|
10
|
+
}
|
|
11
|
+
if (stringCoercion(0) !== '0') {
|
|
12
|
+
throw [0, 'toString', '0'];
|
|
13
|
+
}
|
|
14
|
+
if (stringCoercion(-0) !== '0') {
|
|
15
|
+
throw [0, 'toString', '0'];
|
|
16
|
+
}
|
|
17
|
+
if (stringCoercion(1 / (-0)) !== '-Infinity') {
|
|
18
|
+
throw [0, 'toString', '-Infinity'];
|
|
19
|
+
}
|
|
20
|
+
if (stringCoercion(Infinity) !== 'Infinity') {
|
|
21
|
+
throw [Infinity, 'toString', 'Infinity'];
|
|
22
|
+
}
|
|
23
|
+
if (stringCoercion(-Infinity) !== '-Infinity') {
|
|
24
|
+
throw [-Infinity, 'toString', '-Infinity'];
|
|
25
|
+
}
|
|
26
|
+
if (stringCoercion(1 / -Infinity) !== '0') {
|
|
27
|
+
throw [-Infinity, 'toString', '0'];
|
|
28
|
+
}
|
|
29
|
+
if (stringCoercion(NaN) !== 'NaN') {
|
|
30
|
+
throw [NaN, 'toString', 'NaN'];
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
bool: () => {
|
|
34
|
+
if (stringCoercion(true) !== 'true') {
|
|
35
|
+
throw [true, 'toString', 'true'];
|
|
36
|
+
}
|
|
37
|
+
if (stringCoercion(false) !== 'false') {
|
|
38
|
+
throw [false, 'toString', 'false'];
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
null: () => {
|
|
42
|
+
if (stringCoercion(null) !== 'null') {
|
|
43
|
+
throw [null, 'toString', 'null'];
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
undefined: () => {
|
|
47
|
+
if (stringCoercion(undefined) !== 'undefined') {
|
|
48
|
+
throw [undefined, 'toString', 'undefined'];
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
bigint: () => {
|
|
52
|
+
if (stringCoercion(123n) !== '123') {
|
|
53
|
+
throw [123n, 'toString', '123'];
|
|
54
|
+
}
|
|
55
|
+
if (stringCoercion(-456n) !== '-456') {
|
|
56
|
+
throw [-456n, 'toString', '-456'];
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
array: () => {
|
|
60
|
+
const arr = [1, 2, 3];
|
|
61
|
+
if (stringCoercion(arr) !== '1,2,3') {
|
|
62
|
+
throw [arr, 'toString', '1,2,3'];
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
func: () => {
|
|
66
|
+
const func = () => 5;
|
|
67
|
+
if (typeof stringCoercion(func) !== 'string') {
|
|
68
|
+
throw [func, 'toString'];
|
|
69
|
+
}
|
|
70
|
+
// if (stringCoercion(func) !== '() => 5') { throw [func, 'toString', 'function result'] }
|
|
71
|
+
},
|
|
72
|
+
object: {
|
|
73
|
+
norm: () => {
|
|
74
|
+
const obj = { a: 1, b: 2 };
|
|
75
|
+
if (stringCoercion(obj) !== '[object Object]') {
|
|
76
|
+
throw [obj, 'toString', '[object Object]'];
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
toString: () => {
|
|
80
|
+
const x = { toString: () => 'custom string' };
|
|
81
|
+
if (stringCoercion(x) !== 'custom string') {
|
|
82
|
+
throw [x, 'toString', 'custom string'];
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
toStringThrow: {
|
|
86
|
+
throw: () => {
|
|
87
|
+
const x = { toString: () => { throw new Error('Custom error'); } };
|
|
88
|
+
stringCoercion(x);
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
toStringNotFunc: {
|
|
92
|
+
throw: () => {
|
|
93
|
+
const x = { toString: 'hello' };
|
|
94
|
+
stringCoercion(x);
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
toStringNonPrimitive: {
|
|
98
|
+
throw: () => {
|
|
99
|
+
const x = { toString: () => [] };
|
|
100
|
+
stringCoercion(x);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "functionalscript",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"**/*.js",
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"git-clean": "git clean -xf",
|
|
13
13
|
"test20": "npm run prepack && node --test",
|
|
14
14
|
"test22": "tsc && node --test --experimental-strip-types",
|
|
15
|
-
"test": "tsc && node --test",
|
|
15
|
+
"test": "tsc && node --test --experimental-test-coverage --test-coverage-include=**/module.f.ts",
|
|
16
16
|
"index": "node ./dev/index/module.ts",
|
|
17
17
|
"fsc": "node ./fsc/module.ts",
|
|
18
18
|
"fst": "node ./dev/tf/module.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"url": "git+https://github.com/functionalscript/functionalscript.git"
|
|
31
31
|
},
|
|
32
32
|
"author": "Sergey Shandar",
|
|
33
|
-
"license": "
|
|
33
|
+
"license": "MIT",
|
|
34
34
|
"keywords": [
|
|
35
35
|
"lambda",
|
|
36
36
|
"functional-programming",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
},
|
|
46
46
|
"homepage": "https://github.com/functionalscript/functionalscript#readme",
|
|
47
47
|
"devDependencies": {
|
|
48
|
-
"@types/node": "
|
|
49
|
-
"@typescript/native-preview": "
|
|
50
|
-
"typescript": "
|
|
48
|
+
"@types/node": "*",
|
|
49
|
+
"@typescript/native-preview": "*",
|
|
50
|
+
"typescript": "*"
|
|
51
51
|
}
|
|
52
52
|
}
|
package/bnf/djs/module.f.d.ts
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rules for serializing and deserializing the BNF grammar.
|
|
3
|
-
*
|
|
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.
|
|
30
|
-
*/
|
|
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
|
-
*/
|
|
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
|
-
*/
|
|
41
|
-
export type RuleMap<Id extends string> = {
|
|
42
|
-
readonly [k in Id]: Rule<Id>;
|
|
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
DELETED
|
@@ -1,207 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Rules for serializing and deserializing the BNF grammar.
|
|
3
|
-
*
|
|
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.
|
|
167
|
-
*/
|
|
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
|
-
};
|