functionalscript 0.45.0 → 0.46.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/README.md +5 -3
- package/fjs/asn.1/module.f.mjs +8 -1
- package/fjs/asn.1/proof.f.d.mts +10 -0
- package/fjs/asn.1/proof.f.mjs +16 -0
- package/fjs/basen/base128/module.f.mjs +22 -5
- package/fjs/bnf/data/module.f.d.mts +32 -6
- package/fjs/bnf/data/module.f.mjs +112 -7
- package/fjs/bnf/data/proof.f.d.mts +2 -0
- package/fjs/bnf/data/proof.f.mjs +74 -3
- package/fjs/bnf/data/types.d.ts +20 -2
- package/fjs/bnf/descent/module.f.d.mts +28 -14
- package/fjs/bnf/descent/module.f.mjs +122 -77
- package/fjs/bnf/descent/proof.f.d.mts +2 -0
- package/fjs/bnf/descent/proof.f.mjs +117 -31
- package/fjs/bnf/descent/types.d.ts +12 -14
- package/fjs/bnf/ll1/module.f.d.mts +64 -22
- package/fjs/bnf/ll1/module.f.mjs +214 -154
- package/fjs/bnf/ll1/proof.f.d.mts +15 -2
- package/fjs/bnf/ll1/proof.f.mjs +323 -149
- package/fjs/bnf/ll1/types.d.ts +16 -24
- package/fjs/bnf/matcher/module.f.d.mts +66 -0
- package/fjs/bnf/matcher/module.f.mjs +81 -0
- package/fjs/bnf/matcher/proof.f.d.mts +10 -0
- package/fjs/bnf/matcher/proof.f.mjs +79 -0
- package/fjs/bnf/matcher/types.d.ts +54 -0
- package/fjs/bnf/testlib.f.d.mts +31 -0
- package/fjs/bnf/testlib.f.mjs +80 -0
- package/fjs/cas/cli/module.f.d.mts +1 -1
- package/fjs/cas/cli/module.f.mjs +14 -20
- package/fjs/cas/cli/proof.f.d.mts +1 -3
- package/fjs/cas/cli/proof.f.mjs +44 -33
- package/fjs/cas/evo/module.f.d.mts +64 -18
- package/fjs/cas/evo/module.f.mjs +148 -70
- package/fjs/cas/evo/proof.f.d.mts +10 -1
- package/fjs/cas/evo/proof.f.mjs +305 -223
- package/fjs/cas/evo/types.d.ts +45 -9
- package/fjs/cas/module.f.d.mts +18 -32
- package/fjs/cas/module.f.mjs +129 -128
- package/fjs/cas/proof.f.d.mts +11 -8
- package/fjs/cas/proof.f.mjs +259 -147
- package/fjs/cas/types.d.ts +24 -10
- package/fjs/ci/config/module.f.d.mts +2 -2
- package/fjs/ci/config/module.f.mjs +2 -2
- package/fjs/ci/module.f.d.mts +5 -5
- package/fjs/ci/module.f.mjs +8 -7
- package/fjs/ci/nix/module.f.d.mts +7 -5
- package/fjs/ci/nix/module.f.mjs +13 -12
- package/fjs/ci/nix/proof.f.mjs +2 -2
- package/fjs/ci/proof.f.mjs +9 -5
- package/fjs/cli/module.f.d.mts +4 -6
- package/fjs/cli/module.f.mjs +4 -8
- package/fjs/cli/proof.f.mjs +17 -16
- package/fjs/cli/types.d.ts +2 -3
- package/fjs/common/monoid/types.d.ts +1 -1
- package/fjs/crypto/hmac/module.f.mjs +2 -2
- package/fjs/crypto/sha2/module.f.mjs +3 -1
- package/fjs/crypto/sha2/proof.f.d.mts +1 -0
- package/fjs/crypto/sha2/proof.f.mjs +24 -0
- package/fjs/crypto/sha2/types.d.ts +11 -0
- package/fjs/crypto/sign/module.f.mjs +2 -2
- package/fjs/dev/module.f.d.mts +13 -4
- package/fjs/dev/module.f.mjs +56 -27
- package/fjs/dev/update/module.f.d.mts +9 -4
- package/fjs/dev/update/module.f.mjs +14 -10
- package/fjs/dev/update/proof.f.d.mts +1 -3
- package/fjs/dev/update/proof.f.mjs +10 -5
- package/fjs/djs/module.f.d.mts +13 -5
- package/fjs/djs/module.f.mjs +31 -16
- package/fjs/djs/parser/module.f.d.mts +13 -3
- package/fjs/djs/parser/module.f.mjs +117 -16
- package/fjs/djs/parser/proof.f.d.mts +5 -1
- package/fjs/djs/parser/proof.f.mjs +274 -12
- package/fjs/djs/parser/types.d.ts +7 -1
- package/fjs/djs/proof.f.d.mts +18 -2
- package/fjs/djs/proof.f.mjs +187 -12
- package/fjs/djs/serializer/module.f.d.mts +25 -8
- package/fjs/djs/serializer/module.f.mjs +61 -15
- package/fjs/djs/serializer/proof.f.d.mts +5 -0
- package/fjs/djs/serializer/proof.f.mjs +23 -1
- package/fjs/djs/tokenizer/module.f.d.mts +17 -5
- package/fjs/djs/tokenizer/module.f.mjs +99 -60
- package/fjs/djs/tokenizer/proof.f.d.mts +1 -1
- package/fjs/djs/tokenizer/proof.f.mjs +74 -67
- package/fjs/djs/transpiler/module.f.d.mts +12 -7
- package/fjs/djs/transpiler/module.f.mjs +82 -56
- package/fjs/djs/transpiler/types.d.ts +7 -3
- package/fjs/djs/types.d.ts +7 -1
- package/fjs/effects/list/module.f.d.mts +14 -11
- package/fjs/effects/list/module.f.mjs +13 -11
- package/fjs/effects/list/types.d.ts +27 -7
- package/fjs/effects/memory/module.f.d.mts +2 -1
- package/fjs/effects/memory/module.f.mjs +3 -4
- package/fjs/effects/memory/proof.f.mjs +13 -7
- package/fjs/effects/memory/types.d.ts +4 -3
- package/fjs/effects/mock/module.f.d.mts +20 -4
- package/fjs/effects/mock/module.f.mjs +37 -5
- package/fjs/effects/mock/types.d.ts +10 -1
- package/fjs/effects/module.d.mts +4 -2
- package/fjs/effects/module.f.d.mts +466 -280
- package/fjs/effects/module.f.mjs +537 -299
- package/fjs/effects/module.mjs +2 -1
- package/fjs/effects/node/memory/module.d.mts +4 -2
- package/fjs/effects/node/memory/module.mjs +6 -3
- package/fjs/effects/node/memory/proof.mjs +10 -4
- package/fjs/effects/node/module.d.mts +4 -3
- package/fjs/effects/node/module.f.d.mts +168 -33
- package/fjs/effects/node/module.f.mjs +256 -52
- package/fjs/effects/node/module.mjs +57 -34
- package/fjs/effects/node/proof.f.d.mts +28 -2
- package/fjs/effects/node/proof.f.mjs +161 -42
- package/fjs/effects/node/types.d.ts +106 -18
- package/fjs/effects/node/virtual/module.f.d.mts +18 -4
- package/fjs/effects/node/virtual/module.f.mjs +110 -68
- package/fjs/effects/node/virtual/proof.f.d.mts +28 -2
- package/fjs/effects/node/virtual/proof.f.mjs +190 -9
- package/fjs/effects/proof.f.d.mts +69 -37
- package/fjs/effects/proof.f.mjs +410 -130
- package/fjs/effects/types.d.ts +161 -33
- package/fjs/emergent_testing/module.f.d.mts +19 -12
- package/fjs/emergent_testing/module.f.mjs +93 -33
- package/fjs/emergent_testing/proof.f.d.mts +21 -7
- package/fjs/emergent_testing/proof.f.mjs +166 -32
- package/fjs/emergent_testing/types.d.ts +22 -4
- package/fjs/fsm/module.f.d.mts +14 -4
- package/fjs/fsm/module.f.mjs +54 -37
- package/fjs/fsm/proof.f.d.mts +2 -0
- package/fjs/fsm/proof.f.mjs +83 -114
- package/fjs/js/keywords/module.f.d.mts +52 -0
- package/fjs/js/keywords/module.f.mjs +72 -0
- package/fjs/js/keywords/proof.f.d.mts +3 -0
- package/fjs/js/keywords/proof.f.mjs +13 -0
- package/fjs/js/tokenizer/module.f.d.mts +26 -6
- package/fjs/js/tokenizer/module.f.mjs +145 -159
- package/fjs/js/tokenizer/proof.f.d.mts +1 -0
- package/fjs/js/tokenizer/proof.f.mjs +54 -24
- package/fjs/js/tokenizer/types.d.ts +33 -24
- package/fjs/mcp/cas/module.f.d.mts +1 -6
- package/fjs/mcp/cas/module.f.mjs +55 -51
- package/fjs/mcp/cas/proof.f.d.mts +15 -0
- package/fjs/mcp/cas/proof.f.mjs +174 -0
- package/fjs/mcp/evo/module.f.d.mts +19 -10
- package/fjs/mcp/evo/module.f.mjs +48 -27
- package/fjs/mcp/evo/proof.f.d.mts +6 -1
- package/fjs/mcp/evo/proof.f.mjs +115 -31
- package/fjs/mcp/module.f.d.mts +4 -4
- package/fjs/mcp/module.f.mjs +6 -6
- package/fjs/mcp/proof.f.d.mts +5 -3
- package/fjs/mcp/proof.f.mjs +112 -46
- package/fjs/media/html/module.f.mjs +1 -1
- package/fjs/media/json/extended/module.f.d.mts +82 -0
- package/fjs/media/json/extended/module.f.mjs +153 -0
- package/fjs/media/json/extended/proof.f.d.mts +42 -0
- package/fjs/media/json/extended/proof.f.mjs +127 -0
- package/fjs/media/json/extended/types.d.ts +23 -0
- package/fjs/media/json/module.f.d.mts +8 -2
- package/fjs/media/json/module.f.mjs +43 -41
- package/fjs/media/json/number/module.f.d.mts +59 -0
- package/fjs/media/json/number/module.f.mjs +136 -0
- package/fjs/media/json/number/proof.f.d.mts +24 -0
- package/fjs/media/json/number/proof.f.mjs +86 -0
- package/fjs/media/json/number/types.d.ts +28 -0
- package/fjs/media/json/parser/module.f.d.mts +25 -13
- package/fjs/media/json/parser/module.f.mjs +114 -70
- package/fjs/media/json/parser/proof.f.d.mts +5 -0
- package/fjs/media/json/parser/proof.f.mjs +31 -1
- package/fjs/media/json/parser/types.d.ts +33 -14
- package/fjs/media/json/rtti/module.f.d.mts +1 -1
- package/fjs/media/json/rtti/module.f.mjs +1 -1
- package/fjs/media/json/rtti/proof.f.mjs +9 -9
- package/fjs/media/json/schema/module.f.mjs +3 -13
- package/fjs/media/json/schema/proof.f.d.mts +0 -1
- package/fjs/media/json/schema/proof.f.mjs +1 -2
- package/fjs/media/json/serializer/module.f.d.mts +32 -1
- package/fjs/media/json/serializer/module.f.mjs +64 -2
- package/fjs/media/json/tokenizer/module.f.mjs +7 -3
- package/fjs/media/json/tokenizer/proof.f.d.mts +6 -0
- package/fjs/media/json/tokenizer/proof.f.mjs +62 -21
- package/fjs/media/json/types.d.ts +36 -10
- package/fjs/media/lock/module.f.d.mts +100 -0
- package/fjs/media/lock/module.f.mjs +125 -0
- package/fjs/media/lock/proof.f.d.mts +33 -0
- package/fjs/media/lock/proof.f.mjs +196 -0
- package/fjs/media/lock/types.d.ts +15 -0
- package/fjs/media/module.f.d.mts +6 -5
- package/fjs/media/module.f.mjs +8 -7
- package/fjs/media/note/module.f.d.mts +121 -0
- package/fjs/media/note/module.f.mjs +131 -0
- package/fjs/media/note/proof.f.d.mts +29 -0
- package/fjs/media/note/proof.f.mjs +150 -0
- package/fjs/media/note/types.d.ts +10 -0
- package/fjs/media/proof.f.d.mts +4 -1
- package/fjs/media/proof.f.mjs +40 -21
- package/fjs/media/revision/module.f.d.mts +78 -7
- package/fjs/media/revision/module.f.mjs +119 -12
- package/fjs/media/revision/proof.f.d.mts +10 -0
- package/fjs/media/revision/proof.f.mjs +88 -0
- package/fjs/media/revision/types.d.ts +34 -5
- package/fjs/media/type/module.f.d.mts +33 -15
- package/fjs/media/type/module.f.mjs +35 -29
- package/fjs/media/type/proof.f.d.mts +2 -1
- package/fjs/media/type/proof.f.mjs +30 -10
- package/fjs/module.f.mjs +29 -8
- package/fjs/nanvm/proof.f.mjs +3 -3
- package/fjs/nanvm/rust/module.f.mjs +1 -1
- package/fjs/nanvm/update/module.f.d.mts +4 -4
- package/fjs/nanvm/update/module.f.mjs +8 -9
- package/fjs/nanvm/update/proof.f.mjs +4 -3
- package/fjs/proof.f.d.mts +3 -3
- package/fjs/proof.f.mjs +36 -10
- package/fjs/protocol/json_rpc/module.f.d.mts +2 -2
- package/fjs/protocol/json_rpc/module.f.mjs +3 -3
- package/fjs/protocol/json_rpc/proof.f.mjs +4 -4
- package/fjs/protocol/mcp/module.f.d.mts +10 -13
- package/fjs/protocol/mcp/module.f.mjs +74 -58
- package/fjs/protocol/mcp/proof.f.d.mts +13 -4
- package/fjs/protocol/mcp/proof.f.mjs +193 -61
- package/fjs/protocol/mcp/stdio/module.f.d.mts +15 -7
- package/fjs/protocol/mcp/stdio/module.f.mjs +38 -25
- package/fjs/protocol/mcp/stdio/proof.f.d.mts +4 -2
- package/fjs/protocol/mcp/stdio/proof.f.mjs +45 -12
- package/fjs/protocol/mcp/stdio/types.d.ts +1 -1
- package/fjs/protocol/mcp/types.d.ts +17 -6
- package/fjs/sul/id/module.f.d.mts +0 -1
- package/fjs/sul/id/module.f.mjs +2 -3
- package/fjs/sul/level/hash/module.f.mjs +2 -1
- package/fjs/sul/level/hash/proof.f.mjs +2 -2
- package/fjs/sul/module.f.mjs +18 -13
- package/fjs/text/code_point/module.f.d.mts +8 -0
- package/fjs/text/code_point/module.f.mjs +8 -1
- package/fjs/text/code_point/proof.f.d.mts +1 -0
- package/fjs/text/code_point/proof.f.mjs +11 -0
- package/fjs/text/sgr/module.f.d.mts +4 -6
- package/fjs/text/sgr/module.f.mjs +6 -7
- package/fjs/text/utf16/module.f.mjs +7 -2
- package/fjs/text/utf16/proof.f.mjs +3 -3
- package/fjs/text/utf8/module.f.mjs +4 -2
- package/fjs/types/array/module.f.mjs +14 -2
- package/fjs/types/bit_vec/module.f.d.mts +0 -2
- package/fjs/types/bit_vec/module.f.mjs +46 -32
- package/fjs/types/bit_vec/proof.f.mjs +2 -2
- package/fjs/types/btree/remove/module.f.mjs +1 -1
- package/fjs/types/btree/set/module.f.mjs +9 -11
- package/fjs/types/btree/set/proof.f.mjs +12 -0
- package/fjs/types/byte_set/module.f.d.mts +11 -4
- package/fjs/types/byte_set/module.f.mjs +14 -6
- package/fjs/types/byte_set/proof.f.mjs +7 -7
- package/fjs/types/function/compare/module.f.mjs +10 -3
- package/fjs/types/list/module.f.d.mts +1 -1
- package/fjs/types/list/module.f.mjs +1 -1
- package/fjs/types/nullable/module.f.d.mts +18 -4
- package/fjs/types/nullable/module.f.mjs +21 -4
- package/fjs/types/nullable/proof.f.d.mts +4 -0
- package/fjs/types/nullable/proof.f.mjs +15 -0
- package/fjs/types/object/module.f.d.mts +12 -2
- package/fjs/types/object/module.f.mjs +11 -1
- package/fjs/types/patricia_trie/module.f.mjs +26 -11
- package/fjs/types/result/module.f.d.mts +3 -3
- package/fjs/types/result/module.f.mjs +3 -3
- package/fjs/types/rtti/common/module.f.d.mts +25 -28
- package/fjs/types/rtti/common/module.f.mjs +36 -32
- package/fjs/types/rtti/common/proof.f.mjs +5 -5
- package/fjs/types/rtti/data/module.f.d.mts +14 -0
- package/fjs/types/rtti/data/module.f.mjs +25 -2
- package/fjs/types/rtti/data/proof.f.d.mts +1 -0
- package/fjs/types/rtti/data/proof.f.mjs +25 -2
- package/fjs/types/rtti/parse/module.f.d.mts +24 -14
- package/fjs/types/rtti/parse/module.f.mjs +37 -28
- package/fjs/types/rtti/parse/proof.f.d.mts +3 -2
- package/fjs/types/rtti/parse/proof.f.mjs +33 -14
- package/fjs/types/rtti/proof.f.mjs +3 -1
- package/fjs/types/rtti/ts/module.f.mjs +13 -17
- package/fjs/types/rtti/ts/types.d.ts +14 -1
- package/fjs/types/sorted_set/module.f.d.mts +18 -0
- package/fjs/types/sorted_set/module.f.mjs +22 -0
- package/fjs/types/sorted_set/proof.f.d.mts +1 -0
- package/fjs/types/sorted_set/proof.f.mjs +16 -1
- package/fjs/types/uint8array/module.f.d.mts +1 -1
- package/fjs/types/uint8array/module.f.mjs +1 -1
- package/fjs/website/module.f.d.mts +3 -3
- package/fjs/website/module.f.mjs +4 -7
- package/fjs/website/proof.f.mjs +2 -1
- package/package.json +2 -2
- package/fjs/dev/package_json/module.f.d.mts +0 -39
- package/fjs/dev/package_json/module.f.mjs +0 -40
- package/fjs/dev/package_json/proof.f.d.mts +0 -6
- package/fjs/dev/package_json/proof.f.mjs +0 -32
- package/fjs/effects/eff/module.f.d.mts +0 -20
- package/fjs/effects/eff/module.f.mjs +0 -70
- package/fjs/effects/eff/proof.f.d.mts +0 -15
- package/fjs/effects/eff/proof.f.mjs +0 -69
- package/fjs/effects/eff/types.d.ts +0 -71
- package/fjs/types/rtti/validate/module.f.d.mts +0 -41
- package/fjs/types/rtti/validate/module.f.mjs +0 -171
- package/fjs/types/rtti/validate/proof.f.d.mts +0 -125
- package/fjs/types/rtti/validate/proof.f.mjs +0 -437
- package/fjs/types/rtti/validate/types.d.ts +0 -6
|
@@ -4,13 +4,22 @@
|
|
|
4
4
|
* Built from the serializable IR in `fjs/bnf/data`, this is one member of the
|
|
5
5
|
* family of automaton builders that consume a {@link RuleSet}: it compiles the
|
|
6
6
|
* grammar into a predictive {@link dispatchMap} and matches input into an AST
|
|
7
|
-
* ({@link MatchResult}). It throws at build time
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* ({@link MatchResult}). It throws at build time when the grammar is not
|
|
8
|
+
* LL(1): `can not merge …` for a first/first conflict, `left recursion …` for
|
|
9
|
+
* a rule reachable from itself in first position. Nullability is looked up
|
|
10
|
+
* from {@link emptyTagMap} in `fjs/bnf/data` rather than re-derived here.
|
|
11
|
+
*
|
|
12
|
+
* A rule is entered *before* its first symbol is consumed: the dispatch map
|
|
13
|
+
* only selects — a variant's branch, a repetition's next round — and every
|
|
14
|
+
* rule invocation builds a node of its own, so the AST is the one
|
|
15
|
+
* `fjs/bnf/descent` builds for the same grammar (see ./README.md). A `Repeat`
|
|
16
|
+
* rule is matched iteratively and produces one node holding a flat sequence
|
|
17
|
+
* of the items it matched.
|
|
10
18
|
*
|
|
11
19
|
* The caller passes physical symbols only; the matcher synthesizes the one
|
|
12
|
-
* logical EOF
|
|
13
|
-
*
|
|
20
|
+
* logical EOF after them, so a grammar can dispatch on the end of input with
|
|
21
|
+
* the `eof` terminal. The position it does that at, the AST it builds, and the
|
|
22
|
+
* constructors that pair them are the shared layer in `fjs/bnf/matcher`.
|
|
14
23
|
*
|
|
15
24
|
* See `./types.ts` for the type-level API.
|
|
16
25
|
*
|
|
@@ -19,16 +28,35 @@
|
|
|
19
28
|
* @import { CodePoint } from '../../text/utf16/types.ts'
|
|
20
29
|
* @import { Properties } from '../../types/range_map/types.ts'
|
|
21
30
|
* @import { StringSet } from '../../types/string_set/types.ts'
|
|
22
|
-
* @import {
|
|
31
|
+
* @import { List } from '../../types/list/types.ts'
|
|
32
|
+
* @import { RuleSet, Sequence } from '../data/types.ts'
|
|
33
|
+
* @import { Ast, AstResult, AstSequence, AstTag, Cursor } from '../matcher/types.ts'
|
|
23
34
|
* @import { Rule as FRule } from '../types.ts'
|
|
24
|
-
* @import {
|
|
35
|
+
* @import { Match, MatchResult, Remainder, _Dispatch, _DispatchBranch, _DispatchMap, _DispatchResult, _DispatchRule } from './types.ts'
|
|
25
36
|
*/
|
|
26
|
-
import type {
|
|
37
|
+
import type { CodePoint } from '../../text/utf16/types.ts';
|
|
38
|
+
import type { List } from '../../types/list/types.ts';
|
|
39
|
+
import type { RuleSet, Sequence } from '../data/types.ts';
|
|
40
|
+
import type { Ast, AstResult, AstSequence, AstTag, Cursor } from '../matcher/types.ts';
|
|
27
41
|
import type { Rule as FRule } from '../types.ts';
|
|
28
|
-
import type {
|
|
42
|
+
import type { Match, _DispatchMap } from './types.ts';
|
|
29
43
|
/**
|
|
30
44
|
* Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
|
|
31
45
|
*
|
|
46
|
+
* Each rule's entry holds the rule's first set as a range map. Only a variant
|
|
47
|
+
* reads the value under a symbol — the branch that lookahead selects; every
|
|
48
|
+
* other reader asks whether a symbol is in the set at all, which is how a
|
|
49
|
+
* repetition decides to start another round. `empty` is the branch a variant
|
|
50
|
+
* takes when no entry matches the lookahead — its nullable branch, absent
|
|
51
|
+
* when the variant cannot match empty.
|
|
52
|
+
*
|
|
53
|
+
* A rule referenced in first position while its own first set is still being
|
|
54
|
+
* computed is left recursion — a grammar no lookahead can decide, whose match
|
|
55
|
+
* would loop at the same position forever — so building throws
|
|
56
|
+
* (`left recursion …`) rather than letting the matcher diverge. The one
|
|
57
|
+
* self-reference with a sound reading is a repetition of itself: zero rounds
|
|
58
|
+
* match whatever the item does, so it dispatches on nothing and matches empty.
|
|
59
|
+
*
|
|
32
60
|
* @type {(ruleSet: RuleSet) => _DispatchMap}
|
|
33
61
|
*/
|
|
34
62
|
export declare const dispatchMap: (ruleSet: RuleSet) => _DispatchMap;
|
|
@@ -38,27 +66,41 @@ export declare const dispatchMap: (ruleSet: RuleSet) => _DispatchMap;
|
|
|
38
66
|
* @type {(fr: FRule) => Match}
|
|
39
67
|
*/
|
|
40
68
|
export declare const parser: (fr: FRule) => Match;
|
|
41
|
-
export type
|
|
42
|
-
export type
|
|
43
|
-
export type
|
|
44
|
-
readonly
|
|
45
|
-
readonly
|
|
46
|
-
readonly
|
|
69
|
+
export type _Position = Cursor | null;
|
|
70
|
+
export type _Result = AstResult<CodePoint, _Position>;
|
|
71
|
+
export type _SeqFrame = {
|
|
72
|
+
readonly kind: 'seq';
|
|
73
|
+
readonly tag: AstTag;
|
|
74
|
+
readonly items: Sequence;
|
|
75
|
+
readonly itemIndex: number;
|
|
76
|
+
readonly seq: AstSequence<CodePoint>;
|
|
47
77
|
};
|
|
48
|
-
export type
|
|
78
|
+
export type _RepeatFrame = {
|
|
79
|
+
readonly kind: 'repeat';
|
|
49
80
|
readonly tag: AstTag;
|
|
50
|
-
readonly
|
|
51
|
-
readonly
|
|
52
|
-
readonly seq: AstSequence;
|
|
81
|
+
readonly item: string;
|
|
82
|
+
readonly items: _Items;
|
|
53
83
|
};
|
|
84
|
+
export type _Items = List<Ast<CodePoint>>;
|
|
85
|
+
export type _Frame = _SeqFrame | _RepeatFrame;
|
|
54
86
|
export type _Stack = null | {
|
|
55
87
|
readonly top: _Frame;
|
|
56
88
|
readonly rest: _Stack;
|
|
57
89
|
};
|
|
58
|
-
export type
|
|
59
|
-
readonly
|
|
60
|
-
readonly
|
|
90
|
+
export type _RuleTask = {
|
|
91
|
+
readonly kind: 'rule';
|
|
92
|
+
readonly name: string;
|
|
93
|
+
readonly tag: AstTag;
|
|
94
|
+
readonly pos: Cursor;
|
|
95
|
+
};
|
|
96
|
+
export type _RepeatTask = {
|
|
97
|
+
readonly kind: 'repeat';
|
|
98
|
+
readonly tag: AstTag;
|
|
99
|
+
readonly item: string;
|
|
100
|
+
readonly items: _Items;
|
|
101
|
+
readonly pos: Cursor;
|
|
61
102
|
};
|
|
103
|
+
export type _Task = _RuleTask | _RepeatTask;
|
|
62
104
|
/**
|
|
63
105
|
* Creates an LL(1) parser from an already materialized {@link RuleSet}.
|
|
64
106
|
*
|
package/fjs/bnf/ll1/module.f.mjs
CHANGED
|
@@ -4,13 +4,22 @@
|
|
|
4
4
|
* Built from the serializable IR in `fjs/bnf/data`, this is one member of the
|
|
5
5
|
* family of automaton builders that consume a {@link RuleSet}: it compiles the
|
|
6
6
|
* grammar into a predictive {@link dispatchMap} and matches input into an AST
|
|
7
|
-
* ({@link MatchResult}). It throws at build time
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* ({@link MatchResult}). It throws at build time when the grammar is not
|
|
8
|
+
* LL(1): `can not merge …` for a first/first conflict, `left recursion …` for
|
|
9
|
+
* a rule reachable from itself in first position. Nullability is looked up
|
|
10
|
+
* from {@link emptyTagMap} in `fjs/bnf/data` rather than re-derived here.
|
|
11
|
+
*
|
|
12
|
+
* A rule is entered *before* its first symbol is consumed: the dispatch map
|
|
13
|
+
* only selects — a variant's branch, a repetition's next round — and every
|
|
14
|
+
* rule invocation builds a node of its own, so the AST is the one
|
|
15
|
+
* `fjs/bnf/descent` builds for the same grammar (see ./README.md). A `Repeat`
|
|
16
|
+
* rule is matched iteratively and produces one node holding a flat sequence
|
|
17
|
+
* of the items it matched.
|
|
10
18
|
*
|
|
11
19
|
* The caller passes physical symbols only; the matcher synthesizes the one
|
|
12
|
-
* logical EOF
|
|
13
|
-
*
|
|
20
|
+
* logical EOF after them, so a grammar can dispatch on the end of input with
|
|
21
|
+
* the `eof` terminal. The position it does that at, the AST it builds, and the
|
|
22
|
+
* constructors that pair them are the shared layer in `fjs/bnf/matcher`.
|
|
14
23
|
*
|
|
15
24
|
* See `./types.ts` for the type-level API.
|
|
16
25
|
*
|
|
@@ -19,18 +28,24 @@
|
|
|
19
28
|
* @import { CodePoint } from '../../text/utf16/types.ts'
|
|
20
29
|
* @import { Properties } from '../../types/range_map/types.ts'
|
|
21
30
|
* @import { StringSet } from '../../types/string_set/types.ts'
|
|
22
|
-
* @import {
|
|
31
|
+
* @import { List } from '../../types/list/types.ts'
|
|
32
|
+
* @import { RuleSet, Sequence } from '../data/types.ts'
|
|
33
|
+
* @import { Ast, AstResult, AstSequence, AstTag, Cursor } from '../matcher/types.ts'
|
|
23
34
|
* @import { Rule as FRule } from '../types.ts'
|
|
24
|
-
* @import {
|
|
35
|
+
* @import { Match, MatchResult, Remainder, _Dispatch, _DispatchBranch, _DispatchMap, _DispatchResult, _DispatchRule } from './types.ts'
|
|
25
36
|
*/
|
|
26
37
|
|
|
27
38
|
import { strictEqual } from '../../types/function/operator/module.f.mjs'
|
|
28
|
-
import {
|
|
39
|
+
import { assertNotNullish } from '../../asserts/module.f.mjs'
|
|
40
|
+
import { identity } from '../../types/function/module.f.mjs'
|
|
41
|
+
import { concat, toArray } from '../../types/list/module.f.mjs'
|
|
29
42
|
import { rangeMap } from '../../types/range_map/module.f.mjs'
|
|
43
|
+
import { contains as rangeContains } from '../../types/range/module.f.mjs'
|
|
30
44
|
import { contains, set } from '../../types/string_set/module.f.mjs'
|
|
31
|
-
import {
|
|
45
|
+
import { rangeDecode } from '../module.f.mjs'
|
|
32
46
|
import { definedEntries } from '../../types/object/module.f.mjs'
|
|
33
|
-
import { emptyTagMap, toData } from '../data/module.f.mjs'
|
|
47
|
+
import { emptyTagMap, isRepeat, toData } from '../data/module.f.mjs'
|
|
48
|
+
import { leafAt, mrFail, mrSuccess, physicalIdx, symbolAt } from '../matcher/module.f.mjs'
|
|
34
49
|
|
|
35
50
|
/** @type {Properties<_DispatchResult>} */
|
|
36
51
|
const dispatchProps = {
|
|
@@ -52,83 +67,81 @@ const dispatchOp = rangeMap(dispatchProps)
|
|
|
52
67
|
/**
|
|
53
68
|
* Builds a dispatch map for a {@link RuleSet} to enable predictive parsing.
|
|
54
69
|
*
|
|
70
|
+
* Each rule's entry holds the rule's first set as a range map. Only a variant
|
|
71
|
+
* reads the value under a symbol — the branch that lookahead selects; every
|
|
72
|
+
* other reader asks whether a symbol is in the set at all, which is how a
|
|
73
|
+
* repetition decides to start another round. `empty` is the branch a variant
|
|
74
|
+
* takes when no entry matches the lookahead — its nullable branch, absent
|
|
75
|
+
* when the variant cannot match empty.
|
|
76
|
+
*
|
|
77
|
+
* A rule referenced in first position while its own first set is still being
|
|
78
|
+
* computed is left recursion — a grammar no lookahead can decide, whose match
|
|
79
|
+
* would loop at the same position forever — so building throws
|
|
80
|
+
* (`left recursion …`) rather than letting the matcher diverge. The one
|
|
81
|
+
* self-reference with a sound reading is a repetition of itself: zero rounds
|
|
82
|
+
* match whatever the item does, so it dispatches on nothing and matches empty.
|
|
83
|
+
*
|
|
55
84
|
* @type {(ruleSet: RuleSet) => _DispatchMap}
|
|
56
85
|
*/
|
|
57
86
|
export const dispatchMap = ruleSet => {
|
|
58
87
|
|
|
59
88
|
const nullMap = emptyTagMap(ruleSet)
|
|
60
89
|
|
|
61
|
-
/** @type {(dr: _DispatchResult, name: string) => _DispatchResult} */
|
|
62
|
-
const addRuleToDispatch = (dr, name) => {
|
|
63
|
-
if (dr === null)
|
|
64
|
-
return null
|
|
65
|
-
|
|
66
|
-
return { tag: dr.tag, rules: [...dr.rules, name]}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/** @type {(dr: _DispatchResult, tag: string) => _DispatchResult} */
|
|
70
|
-
const addTagToDispatch = (dr, tag) => {
|
|
71
|
-
if (dr === null)
|
|
72
|
-
return null
|
|
73
|
-
|
|
74
|
-
return { tag, rules: dr.rules}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
90
|
/** @type {(dm: _DispatchMap, name: string, current: StringSet) => _DispatchMap} */
|
|
78
91
|
const dispatchRule = (dm, name, current) => {
|
|
79
92
|
if (name in dm) { return dm }
|
|
80
93
|
const newCurrent = set(name)(current)
|
|
81
94
|
const rule = ruleSet[name]
|
|
95
|
+
/** @type {_DispatchRule} */
|
|
96
|
+
let dr
|
|
82
97
|
if (typeof rule === 'number') {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
return { ...dm, [name]: dr }
|
|
98
|
+
dr = {
|
|
99
|
+
empty: undefined,
|
|
100
|
+
rangeMap: dispatchOp.fromRange({ tag: undefined, name })(rangeDecode(rule)),
|
|
101
|
+
}
|
|
88
102
|
} else if (rule instanceof Array) {
|
|
89
|
-
|
|
90
|
-
|
|
103
|
+
// A sequence's first set is the union of its items' first sets up
|
|
104
|
+
// to and including the first item that cannot match empty.
|
|
91
105
|
/** @type {_Dispatch} */
|
|
92
|
-
let
|
|
106
|
+
let first = []
|
|
93
107
|
for (const item of rule) {
|
|
94
108
|
if (contains(item)(newCurrent)) {
|
|
95
|
-
|
|
96
|
-
} else {
|
|
97
|
-
dm = dispatchRule(dm, item, newCurrent)
|
|
98
|
-
const dr = /** @type {_DispatchRule} */ (dm[item])
|
|
99
|
-
if (emptyTag === true) {
|
|
100
|
-
result = result.map(x => [addRuleToDispatch(x[0], item), x[1]])
|
|
101
|
-
result = toArray(dispatchOp.merge(result)(dr.rangeMap))
|
|
102
|
-
emptyTag = nullMap[item] !== undefined ? true : undefined
|
|
103
|
-
} else {
|
|
104
|
-
result = result.map(x => [addRuleToDispatch(x[0], item), x[1]])
|
|
105
|
-
}
|
|
109
|
+
throw ['left recursion [', name, '][', item, ']']
|
|
106
110
|
}
|
|
111
|
+
dm = dispatchRule(dm, item, newCurrent)
|
|
112
|
+
first = toArray(dispatchOp.merge(first)(assertNotNullish(dm[item]).rangeMap))
|
|
113
|
+
if (nullMap[item] === undefined) { break }
|
|
107
114
|
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
115
|
+
dr = { empty: undefined, rangeMap: first }
|
|
116
|
+
} else if (isRepeat(rule)) {
|
|
117
|
+
// Zero rounds always match, so a repetition needs no `empty`
|
|
118
|
+
// branch; its first set is its item's. A repetition of itself has
|
|
119
|
+
// no first set to dispatch on, so it can only match zero rounds —
|
|
120
|
+
// `toData` never derives one, but a hand-written rule set can.
|
|
121
|
+
dm = contains(rule)(newCurrent) ? dm : dispatchRule(dm, rule, newCurrent)
|
|
122
|
+
const itemDr = dm[rule]
|
|
123
|
+
dr = { empty: undefined, rangeMap: itemDr === undefined ? [] : itemDr.rangeMap }
|
|
111
124
|
} else {
|
|
112
|
-
const entries = definedEntries(rule)
|
|
113
125
|
/** @type {_Dispatch} */
|
|
114
|
-
let
|
|
115
|
-
/** @type {
|
|
116
|
-
let
|
|
117
|
-
for (const [tag, item] of
|
|
126
|
+
let first = []
|
|
127
|
+
/** @type {_DispatchBranch | undefined} */
|
|
128
|
+
let empty = undefined
|
|
129
|
+
for (const [tag, item] of definedEntries(rule)) {
|
|
130
|
+
if (contains(item)(newCurrent)) {
|
|
131
|
+
throw ['left recursion [', name, '][', item, ']']
|
|
132
|
+
}
|
|
118
133
|
dm = dispatchRule(dm, item, newCurrent)
|
|
119
|
-
|
|
134
|
+
/** @type {_Dispatch} */
|
|
135
|
+
const d = assertNotNullish(dm[item]).rangeMap
|
|
136
|
+
.map(x => [x[0] === null ? null : { tag, name: item }, x[1]])
|
|
137
|
+
first = toArray(dispatchOp.merge(first)(d))
|
|
120
138
|
if (nullMap[item] !== undefined) {
|
|
121
|
-
|
|
122
|
-
} else {
|
|
123
|
-
/** @type {_Dispatch} */
|
|
124
|
-
const d = dr.rangeMap.map(x => [addTagToDispatch(x[0], tag), x[1]])
|
|
125
|
-
result = toArray(dispatchOp.merge(result)(d))
|
|
139
|
+
empty = { tag, name: item }
|
|
126
140
|
}
|
|
127
141
|
}
|
|
128
|
-
|
|
129
|
-
const dr = {emptyTag, rangeMap: result}
|
|
130
|
-
return { ...dm, [name]: dr}
|
|
142
|
+
dr = { empty, rangeMap: first }
|
|
131
143
|
}
|
|
144
|
+
return { ...dm, [name]: dr }
|
|
132
145
|
}
|
|
133
146
|
|
|
134
147
|
/** @type {_DispatchMap} */
|
|
@@ -151,48 +164,52 @@ export const parser = fr => {
|
|
|
151
164
|
}
|
|
152
165
|
|
|
153
166
|
/**
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
* consumed.
|
|
167
|
+
* Where a match stopped: a {@link Cursor}, or `null` when it ran out of input —
|
|
168
|
+
* the `null` {@link Remainder} this backend reports for that.
|
|
157
169
|
*
|
|
158
|
-
*
|
|
159
|
-
* can only be true at the physical end, so the pair and the extended position
|
|
160
|
-
* hold the same information.
|
|
161
|
-
*
|
|
162
|
-
* @typedef {number} _Cursor
|
|
170
|
+
* @typedef {Cursor|null} _Position
|
|
163
171
|
*/
|
|
164
172
|
|
|
165
173
|
/**
|
|
166
|
-
*
|
|
167
|
-
*
|
|
174
|
+
* The machine's own result: a {@link MatchResult} positioned by a cursor
|
|
175
|
+
* instead of by a materialized remainder.
|
|
168
176
|
*
|
|
169
|
-
* @typedef {
|
|
177
|
+
* @typedef {AstResult<CodePoint, _Position>} _Result
|
|
170
178
|
*/
|
|
171
179
|
|
|
172
180
|
/**
|
|
173
|
-
*
|
|
174
|
-
*
|
|
181
|
+
* A suspended sequence match: `items[itemIndex]` is being matched by the
|
|
182
|
+
* current task, and `seq` holds the ASTs of the items already matched.
|
|
175
183
|
*
|
|
176
184
|
* @typedef {{
|
|
177
|
-
* readonly
|
|
178
|
-
* readonly
|
|
179
|
-
* readonly
|
|
180
|
-
*
|
|
185
|
+
* readonly kind: 'seq'
|
|
186
|
+
* readonly tag: AstTag
|
|
187
|
+
* readonly items: Sequence
|
|
188
|
+
* readonly itemIndex: number
|
|
189
|
+
* readonly seq: AstSequence<CodePoint>
|
|
190
|
+
* }} _SeqFrame
|
|
181
191
|
*/
|
|
182
192
|
|
|
183
193
|
/**
|
|
184
|
-
* A suspended
|
|
185
|
-
*
|
|
186
|
-
*
|
|
194
|
+
* A suspended repetition: the item is being matched by the current task for
|
|
195
|
+
* one more round, and `items` holds the ASTs of the rounds that already
|
|
196
|
+
* completed. They accumulate as a list rather than an array because a
|
|
197
|
+
* repetition is as long as its input: appending to an array per round would
|
|
198
|
+
* copy the whole prefix each time and make one repetition quadratic in the
|
|
199
|
+
* number of items it matched.
|
|
187
200
|
*
|
|
188
201
|
* @typedef {{
|
|
202
|
+
* readonly kind: 'repeat'
|
|
189
203
|
* readonly tag: AstTag
|
|
190
|
-
* readonly
|
|
191
|
-
* readonly
|
|
192
|
-
*
|
|
193
|
-
* }} _Frame
|
|
204
|
+
* readonly item: string
|
|
205
|
+
* readonly items: _Items
|
|
206
|
+
* }} _RepeatFrame
|
|
194
207
|
*/
|
|
195
208
|
|
|
209
|
+
/** @typedef {List<Ast<CodePoint>>} _Items */
|
|
210
|
+
|
|
211
|
+
/** @typedef {_SeqFrame | _RepeatFrame} _Frame */
|
|
212
|
+
|
|
196
213
|
/**
|
|
197
214
|
* Immutable cons-cell stack: O(1) push/pop, no array copying per step.
|
|
198
215
|
*
|
|
@@ -207,36 +224,39 @@ export const parser = fr => {
|
|
|
207
224
|
* to resume the innermost frame instead.
|
|
208
225
|
*
|
|
209
226
|
* @typedef {{
|
|
210
|
-
* readonly
|
|
211
|
-
* readonly
|
|
212
|
-
*
|
|
227
|
+
* readonly kind: 'rule'
|
|
228
|
+
* readonly name: string
|
|
229
|
+
* readonly tag: AstTag
|
|
230
|
+
* readonly pos: Cursor
|
|
231
|
+
* }} _RuleTask
|
|
213
232
|
*/
|
|
214
233
|
|
|
215
|
-
/** @type {(tag: AstTag, sequence: AstSequence, pos: _Position) => _Result} */
|
|
216
|
-
const mrSuccess = (tag, sequence, pos) =>
|
|
217
|
-
({ast: {tag, sequence}, success: true, pos})
|
|
218
|
-
|
|
219
|
-
/** @type {(tag: AstTag, sequence: AstSequence, pos: _Position) => _Result} */
|
|
220
|
-
const mrFail = (tag, sequence, pos) =>
|
|
221
|
-
({ast: {tag, sequence}, success: false, pos})
|
|
222
|
-
|
|
223
234
|
/**
|
|
224
|
-
* The
|
|
225
|
-
*
|
|
226
|
-
*
|
|
235
|
+
* The next round of a repetition, about to be decided by lookahead. Both the
|
|
236
|
+
* rule that introduces a repetition and the frame that finishes one of its
|
|
237
|
+
* rounds go through this, so a round is set up in exactly one place.
|
|
227
238
|
*
|
|
228
|
-
* @
|
|
239
|
+
* @typedef {{
|
|
240
|
+
* readonly kind: 'repeat'
|
|
241
|
+
* readonly tag: AstTag
|
|
242
|
+
* readonly item: string
|
|
243
|
+
* readonly items: _Items
|
|
244
|
+
* readonly pos: Cursor
|
|
245
|
+
* }} _RepeatTask
|
|
229
246
|
*/
|
|
230
|
-
|
|
247
|
+
|
|
248
|
+
/** @typedef {_RuleTask | _RepeatTask} _Task */
|
|
231
249
|
|
|
232
250
|
/**
|
|
233
|
-
*
|
|
234
|
-
*
|
|
235
|
-
*
|
|
251
|
+
* A leaf here is the code point itself, so it *is* its own symbol. The
|
|
252
|
+
* annotation pins `identity`'s type parameter, which `symbolAt`'s own cannot
|
|
253
|
+
* infer from a fully generic argument.
|
|
236
254
|
*
|
|
237
|
-
* @type {(
|
|
255
|
+
* @type {(leaf: CodePoint) => number}
|
|
238
256
|
*/
|
|
239
|
-
const
|
|
257
|
+
const symbolOf = identity
|
|
258
|
+
|
|
259
|
+
const symbolAtCp = symbolAt(symbolOf)
|
|
240
260
|
|
|
241
261
|
/**
|
|
242
262
|
* The public remainder of a position. It stays physical, so consuming EOF
|
|
@@ -244,7 +264,7 @@ const leafAt = (cp, pos) => pos < cp.length ? [cp[pos]] : []
|
|
|
244
264
|
*
|
|
245
265
|
* @type {(cp: readonly CodePoint[], pos: _Position) => Remainder}
|
|
246
266
|
*/
|
|
247
|
-
const remainderAt = (cp, pos) => pos === null ? null : cp.slice(
|
|
267
|
+
const remainderAt = (cp, pos) => pos === null ? null : cp.slice(physicalIdx(cp.length)(pos))
|
|
248
268
|
|
|
249
269
|
/**
|
|
250
270
|
* Creates an LL(1) parser from an already materialized {@link RuleSet}.
|
|
@@ -255,58 +275,87 @@ export const parserRuleSet = ruleSet => {
|
|
|
255
275
|
const map = dispatchMap(ruleSet)
|
|
256
276
|
|
|
257
277
|
/** @type {(name: string) => _DispatchRule} */
|
|
258
|
-
const dispatched = name =>
|
|
278
|
+
const dispatched = name => assertNotNullish(map[name])
|
|
259
279
|
|
|
260
|
-
// The matcher as an explicit-stack machine
|
|
261
|
-
//
|
|
262
|
-
//
|
|
263
|
-
//
|
|
264
|
-
//
|
|
265
|
-
//
|
|
266
|
-
// depth used to grow with input length and overflow (see the longInput proof
|
|
280
|
+
// The matcher as an explicit-stack machine, the same shape as the sibling
|
|
281
|
+
// `bnf/descent` matcher's: each iteration either starts the current task
|
|
282
|
+
// (walking one rule of the grammar) or feeds the pending result into the
|
|
283
|
+
// innermost frame. The JS call stack stays O(1) however deeply the grammar
|
|
284
|
+
// recurses — nesting depth grows with input length, so a few thousand code
|
|
285
|
+
// points used to overflow a recursive matcher (see the longInput proof
|
|
267
286
|
// group). Positions are cursors into the shared input array for the same
|
|
268
287
|
// reason: re-slicing the remainder per step made a match quadratic.
|
|
269
|
-
|
|
270
|
-
|
|
288
|
+
//
|
|
289
|
+
// Unlike `bnf/descent` it never backtracks: the dispatch map decides every
|
|
290
|
+
// choice from the lookahead symbol, so a cursor only moves forward and no
|
|
291
|
+
// frame needs rewind state.
|
|
292
|
+
/** @type {(name: string, cp: readonly CodePoint[]) => MatchResult} */
|
|
293
|
+
const f = (name, cp) => {
|
|
271
294
|
/** @type {_Stack} */
|
|
272
295
|
let stack = null
|
|
273
296
|
/** @type {_Task|null} */
|
|
274
|
-
let task = {rule:
|
|
297
|
+
let task = {kind: 'rule', name, tag: undefined, pos: 0}
|
|
275
298
|
/** @type {_Result} */
|
|
276
299
|
let result = mrFail(undefined, [], 0)
|
|
277
300
|
|
|
278
301
|
while (true) {
|
|
279
302
|
if (task !== null) {
|
|
280
|
-
// The explicit
|
|
303
|
+
// The explicit cast cuts a control-flow inference cycle
|
|
281
304
|
// (TS7022): `task`'s narrowed type feeds `pos`, which feeds the
|
|
282
305
|
// `task` assignment below that the narrowing depends on.
|
|
283
|
-
/** @type {_Task} */
|
|
284
|
-
const current = task
|
|
285
|
-
const {rule: {emptyTag, rangeMap}, pos} = current
|
|
306
|
+
const current = /** @type {_Task} */ (task)
|
|
286
307
|
task = null
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
result = mrSuccess(emptyTag, [], null)
|
|
308
|
+
if (current.kind === 'repeat') {
|
|
309
|
+
const {tag, item, items, pos} = current
|
|
310
|
+
// One more round starts exactly while the lookahead is in
|
|
311
|
+
// the item's first set. Predictive selection cannot start a
|
|
312
|
+
// round that consumes nothing, so every round advances the
|
|
313
|
+
// cursor and the repetition always terminates.
|
|
314
|
+
if (pos <= cp.length && dispatchOp.get(dispatched(item).rangeMap)(symbolAtCp(cp, pos)) !== null) {
|
|
315
|
+
stack = {top: {kind: 'repeat', tag, item, items}, rest: stack}
|
|
316
|
+
task = {kind: 'rule', name: item, tag: undefined, pos}
|
|
297
317
|
} else {
|
|
298
|
-
result =
|
|
318
|
+
result = mrSuccess(tag, toArray(items), pos)
|
|
299
319
|
}
|
|
300
320
|
continue
|
|
301
321
|
}
|
|
302
|
-
const {tag,
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
322
|
+
const {name, tag, pos} = current
|
|
323
|
+
const rule = ruleSet[name]
|
|
324
|
+
if (typeof rule === 'number') {
|
|
325
|
+
// The one logical EOF is available at the physical end, and
|
|
326
|
+
// only there: a terminal that matches it consumes it, once.
|
|
327
|
+
// Past it no symbol is left to consume.
|
|
328
|
+
if (pos <= cp.length && rangeContains(...rangeDecode(rule))(symbolAtCp(cp, pos))) {
|
|
329
|
+
result = mrSuccess(tag, leafAt(cp, pos), pos + 1)
|
|
330
|
+
} else if (pos >= cp.length) {
|
|
331
|
+
// Nothing left to reject: the match ran out of input.
|
|
332
|
+
result = mrSuccess(undefined, [], null)
|
|
333
|
+
} else {
|
|
334
|
+
result = mrFail(undefined, [], pos)
|
|
335
|
+
}
|
|
336
|
+
} else if (rule instanceof Array) {
|
|
337
|
+
if (rule.length === 0) {
|
|
338
|
+
result = mrSuccess(tag, [], pos)
|
|
339
|
+
} else {
|
|
340
|
+
stack = {top: {kind: 'seq', tag, items: rule, itemIndex: 0, seq: []}, rest: stack}
|
|
341
|
+
task = {kind: 'rule', name: rule[0], tag: undefined, pos}
|
|
342
|
+
}
|
|
343
|
+
} else if (isRepeat(rule)) {
|
|
344
|
+
task = {kind: 'repeat', tag, item: rule, items: null, pos}
|
|
307
345
|
} else {
|
|
308
|
-
|
|
309
|
-
|
|
346
|
+
const {empty, rangeMap} = dispatched(name)
|
|
347
|
+
const d = pos > cp.length ? null : dispatchOp.get(rangeMap)(symbolAtCp(cp, pos))
|
|
348
|
+
const branch = d ?? empty
|
|
349
|
+
if (branch !== undefined) {
|
|
350
|
+
// The selected branch's node becomes the variant's,
|
|
351
|
+
// carrying the branch's tag — entered before its first
|
|
352
|
+
// symbol is consumed, like every rule invocation.
|
|
353
|
+
task = {kind: 'rule', name: branch.name, tag: branch.tag, pos}
|
|
354
|
+
} else if (pos >= cp.length) {
|
|
355
|
+
result = mrSuccess(undefined, [], null)
|
|
356
|
+
} else {
|
|
357
|
+
result = mrFail(undefined, [], pos)
|
|
358
|
+
}
|
|
310
359
|
}
|
|
311
360
|
continue
|
|
312
361
|
}
|
|
@@ -319,23 +368,34 @@ export const parserRuleSet = ruleSet => {
|
|
|
319
368
|
stack = stack.rest
|
|
320
369
|
|
|
321
370
|
const {ast, success, pos} = result
|
|
322
|
-
// A failing
|
|
323
|
-
//
|
|
371
|
+
// A failing rule fails everything above it: LL(1) committed to
|
|
372
|
+
// every choice on the way here, so there is nothing to rewind and
|
|
373
|
+
// retry, and the failure propagates unchanged — its position is
|
|
374
|
+
// where matching stopped.
|
|
324
375
|
if (success === false) { continue }
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
376
|
+
if (frame.kind === 'seq') {
|
|
377
|
+
const seq = [...frame.seq, ast]
|
|
378
|
+
if (pos === null) {
|
|
379
|
+
result = mrSuccess(frame.tag, seq, null)
|
|
380
|
+
} else {
|
|
381
|
+
const itemIndex = frame.itemIndex + 1
|
|
382
|
+
if (itemIndex < frame.items.length) {
|
|
383
|
+
stack = {top: {...frame, itemIndex, seq}, rest: stack}
|
|
384
|
+
task = {kind: 'rule', name: frame.items[itemIndex], tag: undefined, pos}
|
|
385
|
+
} else {
|
|
386
|
+
result = mrSuccess(frame.tag, seq, pos)
|
|
387
|
+
}
|
|
388
|
+
}
|
|
334
389
|
} else {
|
|
335
|
-
|
|
390
|
+
const items = concat(frame.items)([ast])
|
|
391
|
+
if (pos === null) {
|
|
392
|
+
result = mrSuccess(frame.tag, toArray(items), null)
|
|
393
|
+
} else {
|
|
394
|
+
task = {kind: 'repeat', tag: frame.tag, item: frame.item, items, pos}
|
|
395
|
+
}
|
|
336
396
|
}
|
|
337
397
|
}
|
|
338
398
|
}
|
|
339
399
|
|
|
340
|
-
return
|
|
400
|
+
return f
|
|
341
401
|
}
|