functionalscript 0.45.0 → 0.46.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/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/rtti/validate/module.f.d.mts +81 -19
- package/fjs/types/rtti/validate/module.f.mjs +107 -61
- package/fjs/types/rtti/validate/proof.f.d.mts +13 -10
- package/fjs/types/rtti/validate/proof.f.mjs +186 -197
- 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/types.d.ts +0 -6
package/README.md
CHANGED
|
@@ -5,13 +5,15 @@
|
|
|
5
5
|
FunctionalScript is a safe, purely functional programming language and a strict subset of
|
|
6
6
|
[ECMAScript](https://en.wikipedia.org/wiki/ECMAScript)/[JavaScript](https://en.wikipedia.org/wiki/JavaScript). It's inspired by
|
|
7
7
|
|
|
8
|
-
- [JSON](https://en.wikipedia.org/wiki/JSON) and [JSON5](https://json5.org/) as subsets of JavaScript.
|
|
9
|
-
JSON is also a subset of FunctionalScript.
|
|
8
|
+
- [JSON](https://en.wikipedia.org/wiki/JSON) and [JSON5](https://json5.org/) as subsets of JavaScript values.
|
|
9
|
+
JSON is also a subset of FunctionalScript values.
|
|
10
10
|
- [asm.JS](https://en.wikipedia.org/wiki/Asm.js) (a precursor of [WebAssembly](https://en.wikipedia.org/wiki/WebAssembly)),
|
|
11
11
|
as a subset of JavaScript.
|
|
12
12
|
- [TypeScript](https://en.wikipedia.org/wiki/TypeScript), as a superset of JavaScript.
|
|
13
13
|
|
|
14
|
-
[
|
|
14
|
+
[The FunctionalScript specification](./spec/README.md) describes the language
|
|
15
|
+
the compiler accepts today; features not implemented yet are in
|
|
16
|
+
[`spec/todo/`](./spec/todo/README.md).
|
|
15
17
|
|
|
16
18
|
Learn more about
|
|
17
19
|
|
package/fjs/asn.1/module.f.mjs
CHANGED
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
vec,
|
|
20
20
|
vec8,
|
|
21
21
|
} from '../types/bit_vec/module.f.mjs'
|
|
22
|
+
import { assert } from '../asserts/module.f.mjs'
|
|
22
23
|
import { identity } from '../types/function/module.f.mjs'
|
|
23
24
|
import { max } from '../types/function/compare/module.f.mjs'
|
|
24
25
|
import { encode as b128encode, decode as b128decode } from '../basen/base128/module.f.mjs'
|
|
@@ -65,7 +66,13 @@ const parsedTagEncode = ([classPc, number]) => {
|
|
|
65
66
|
/** @type {(v: Vec) => readonly[_ParsedTag, Vec]} */
|
|
66
67
|
const parsedTagDecode = v => {
|
|
67
68
|
const [firstByte, rest] = pop8(v)
|
|
68
|
-
const classPc =
|
|
69
|
+
const classPc = firstByte & classPcMask
|
|
70
|
+
// `classPcMask` is the top three bits, so the result is one of eight values;
|
|
71
|
+
// the assert is what narrows `bigint` to `_ClassPc`.
|
|
72
|
+
assert(classPc === 0b000_00000n || classPc === 0b001_00000n
|
|
73
|
+
|| classPc === 0b010_00000n || classPc === 0b011_00000n
|
|
74
|
+
|| classPc === 0b100_00000n || classPc === 0b101_00000n
|
|
75
|
+
|| classPc === 0b110_00000n || classPc === 0b111_00000n, classPc)
|
|
69
76
|
const firstByteNumber = firstByte & tagNumberMask
|
|
70
77
|
const [number, rest1] = firstByteNumber < tagNumberMask
|
|
71
78
|
? [firstByteNumber, rest]
|
package/fjs/asn.1/proof.f.d.mts
CHANGED
|
@@ -49,5 +49,15 @@ export declare const proof: {
|
|
|
49
49
|
withArc: () => void;
|
|
50
50
|
};
|
|
51
51
|
unknownTag: () => void;
|
|
52
|
+
tagClass: {
|
|
53
|
+
universalPrimitive: () => void;
|
|
54
|
+
universalConstructed: () => void;
|
|
55
|
+
applicationPrimitive: () => void;
|
|
56
|
+
applicationConstructed: () => void;
|
|
57
|
+
contextPrimitive: () => void;
|
|
58
|
+
contextConstructed: () => void;
|
|
59
|
+
privatePrimitive: () => void;
|
|
60
|
+
privateConstructed: () => void;
|
|
61
|
+
};
|
|
52
62
|
raw: (() => void)[];
|
|
53
63
|
};
|
package/fjs/asn.1/proof.f.mjs
CHANGED
|
@@ -296,6 +296,22 @@ export const proof = {
|
|
|
296
296
|
// Re-encoding an UnsupportedRecord returns it unchanged (it is already the TLV bytes)
|
|
297
297
|
assertEq(encode(decoded), raw, 'encode should round-trip UnsupportedRecord')
|
|
298
298
|
},
|
|
299
|
+
// parsedTagDecode narrows the top three bits of the tag's first byte
|
|
300
|
+
// (class + primitive/constructed) to one of exactly eight values via an
|
|
301
|
+
// 8-way `||` assert. Every other test in this file only ever exercises
|
|
302
|
+
// the universal-primitive tag (top bits 000), so each of the other seven
|
|
303
|
+
// comparisons has never had a case where it's the one that turns the
|
|
304
|
+
// assert true — one round trip per class×P/C combination covers them all.
|
|
305
|
+
tagClass: {
|
|
306
|
+
universalPrimitive: () => check(0x02n, vec8(0x42n), empty),
|
|
307
|
+
universalConstructed: () => check(0x22n, vec8(0x42n), empty),
|
|
308
|
+
applicationPrimitive: () => check(0x42n, vec8(0x42n), empty),
|
|
309
|
+
applicationConstructed: () => check(0x62n, vec8(0x42n), empty),
|
|
310
|
+
contextPrimitive: () => check(0x82n, vec8(0x42n), empty),
|
|
311
|
+
contextConstructed: () => check(0xA2n, vec8(0x42n), empty),
|
|
312
|
+
privatePrimitive: () => check(0xC2n, vec8(0x42n), empty),
|
|
313
|
+
privateConstructed: () => check(0xE2n, vec8(0x42n), empty),
|
|
314
|
+
},
|
|
299
315
|
raw: [
|
|
300
316
|
() => {
|
|
301
317
|
const e = encodeRaw([0x00n, vec8(0x23n)])
|
|
@@ -6,12 +6,27 @@
|
|
|
6
6
|
* @import { Vec } from '../../types/bit_vec/types.ts'
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { mask } from '../../types/bigint/module.f.mjs'
|
|
9
10
|
import { vec8, msb, empty } from '../../types/bit_vec/module.f.mjs'
|
|
10
11
|
|
|
11
12
|
const { concat, popFront } = msb
|
|
12
13
|
|
|
13
14
|
const pop8 = popFront(8n)
|
|
14
15
|
|
|
16
|
+
/**
|
|
17
|
+
* The varint layout both halves below agree on, stated once: each byte carries
|
|
18
|
+
* seven payload bits in its low end, and its top bit is set on every byte
|
|
19
|
+
* except the last.
|
|
20
|
+
*
|
|
21
|
+
* `encode` and `decode` are exact inverses, so the two must agree bit for bit.
|
|
22
|
+
* The masks are derived from `payloadBits` rather than written as `0x7f` and
|
|
23
|
+
* `0x80` so that agreement is structural rather than a pair of literals a
|
|
24
|
+
* reader has to match up.
|
|
25
|
+
*/
|
|
26
|
+
const payloadBits = 7n
|
|
27
|
+
const payloadMask = mask(payloadBits)
|
|
28
|
+
const continuationFlag = 1n << payloadBits
|
|
29
|
+
|
|
15
30
|
/**
|
|
16
31
|
* Encodes a bigint into an MSB Base128 vector.
|
|
17
32
|
*
|
|
@@ -22,10 +37,12 @@ export const encode = uint => {
|
|
|
22
37
|
/** @type {Vec} */
|
|
23
38
|
let result = empty
|
|
24
39
|
while (true) {
|
|
25
|
-
const item = uint &
|
|
26
|
-
|
|
40
|
+
const item = uint & payloadMask
|
|
41
|
+
// The first byte built is the last one emitted, so it is the only one
|
|
42
|
+
// without the continuation flag.
|
|
43
|
+
const flag = result === empty ? 0n : continuationFlag
|
|
27
44
|
result = concat(vec8(flag | item))(result)
|
|
28
|
-
uint >>=
|
|
45
|
+
uint >>= payloadBits
|
|
29
46
|
if (uint === 0n) {
|
|
30
47
|
return result
|
|
31
48
|
}
|
|
@@ -42,8 +59,8 @@ export const decode = v => {
|
|
|
42
59
|
let result = 0n
|
|
43
60
|
while (true) {
|
|
44
61
|
const [byte, rest] = pop8(v)
|
|
45
|
-
result = (result <<
|
|
46
|
-
if (byte
|
|
62
|
+
result = (result << payloadBits) | (byte & payloadMask)
|
|
63
|
+
if ((byte & continuationFlag) === 0n) {
|
|
47
64
|
return [result, rest]
|
|
48
65
|
}
|
|
49
66
|
v = rest
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* {@link toData} conversion from the functional grammar.
|
|
4
4
|
*
|
|
5
5
|
* This module is the pure, parser-agnostic substrate: it defines the
|
|
6
|
-
* {@link RuleSet} data form (`Rule = Variant | Sequence | TerminalRange
|
|
7
|
-
* converts a functional grammar into it. The automaton builders
|
|
8
|
-
* {@link RuleSet} live in their own sibling modules
|
|
9
|
-
* `fjs/bnf/descent`, …), so the IR stays free of any one
|
|
6
|
+
* {@link RuleSet} data form (`Rule = Variant | Sequence | TerminalRange |
|
|
7
|
+
* Repeat`) and converts a functional grammar into it. The automaton builders
|
|
8
|
+
* that consume a {@link RuleSet} live in their own sibling modules
|
|
9
|
+
* (`fjs/bnf/ll1`, `fjs/bnf/descent`, …), so the IR stays free of any one
|
|
10
|
+
* parser's machinery.
|
|
10
11
|
*
|
|
11
12
|
* See `./types.ts` for the type-level API.
|
|
12
13
|
*
|
|
@@ -14,11 +15,23 @@
|
|
|
14
15
|
*
|
|
15
16
|
* @import { DataRule, Rule as FRule, Sequence as FSequence } from '../types.ts'
|
|
16
17
|
* @import { StringMap } from '../../types/object/types.ts'
|
|
17
|
-
* @import {
|
|
18
|
+
* @import { StringSet } from '../../types/string_set/types.ts'
|
|
19
|
+
* @import { EmptyTag, Repeat, Rule, RuleSet, Sequence, Variant } from './types.ts'
|
|
18
20
|
*/
|
|
19
21
|
import type { Rule as FRule } from '../types.ts';
|
|
20
22
|
import type { StringMap } from '../../types/object/types.ts';
|
|
21
|
-
import type { EmptyTag, Rule, RuleSet } from './types.ts';
|
|
23
|
+
import type { EmptyTag, Repeat, Rule, RuleSet } from './types.ts';
|
|
24
|
+
/**
|
|
25
|
+
* Whether a data rule is a {@link Repeat}.
|
|
26
|
+
*
|
|
27
|
+
* Every rule-dispatch site asks this instead of testing for a string inline, so
|
|
28
|
+
* the repetition rule kind has exactly one discriminator to move when the
|
|
29
|
+
* surrounding rule model changes.
|
|
30
|
+
*
|
|
31
|
+
* @param {Rule} rule
|
|
32
|
+
* @returns {rule is Repeat}
|
|
33
|
+
*/
|
|
34
|
+
export declare const isRepeat: (rule: Rule) => rule is Repeat;
|
|
22
35
|
export type _EmptyTagMap = StringMap<EmptyTag>;
|
|
23
36
|
/**
|
|
24
37
|
* Computes, for every rule in the set, whether it can match empty input, by
|
|
@@ -38,6 +51,19 @@ export type _EmptyTagMap = StringMap<EmptyTag>;
|
|
|
38
51
|
export declare const emptyTagMap: (ruleSet: RuleSet) => _EmptyTagMap;
|
|
39
52
|
export type _FRuleMap = StringMap<FRule>;
|
|
40
53
|
export type _NewRule = (m: _FRuleMap) => readonly [_FRuleMap, RuleSet, Rule];
|
|
54
|
+
/**
|
|
55
|
+
* Rewrites every right-recursive 0-or-more rule of a {@link RuleSet} into a
|
|
56
|
+
* {@link Repeat}, and drops the rules that the rewrite orphans.
|
|
57
|
+
*
|
|
58
|
+
* The dropped rules are the recursive branch and, unless something else still
|
|
59
|
+
* refers to it, the empty one: both are reachable only through the variant that
|
|
60
|
+
* the {@link Repeat} replaces. Pruning them keeps the invariant that every rule
|
|
61
|
+
* of a generated set is reachable from its entry, so the serialized form holds
|
|
62
|
+
* no dead grammar.
|
|
63
|
+
*
|
|
64
|
+
* @type {(ruleSet: RuleSet, entry: string) => RuleSet}
|
|
65
|
+
*/
|
|
66
|
+
export declare const detectRepeat: (ruleSet: RuleSet, entry: string) => RuleSet;
|
|
41
67
|
/**
|
|
42
68
|
* Converts a functional grammar rule into serializable BNF data and returns
|
|
43
69
|
* the generated rule set with the entry rule identifier.
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* {@link toData} conversion from the functional grammar.
|
|
4
4
|
*
|
|
5
5
|
* This module is the pure, parser-agnostic substrate: it defines the
|
|
6
|
-
* {@link RuleSet} data form (`Rule = Variant | Sequence | TerminalRange
|
|
7
|
-
* converts a functional grammar into it. The automaton builders
|
|
8
|
-
* {@link RuleSet} live in their own sibling modules
|
|
9
|
-
* `fjs/bnf/descent`, …), so the IR stays free of any one
|
|
6
|
+
* {@link RuleSet} data form (`Rule = Variant | Sequence | TerminalRange |
|
|
7
|
+
* Repeat`) and converts a functional grammar into it. The automaton builders
|
|
8
|
+
* that consume a {@link RuleSet} live in their own sibling modules
|
|
9
|
+
* (`fjs/bnf/ll1`, `fjs/bnf/descent`, …), so the IR stays free of any one
|
|
10
|
+
* parser's machinery.
|
|
10
11
|
*
|
|
11
12
|
* See `./types.ts` for the type-level API.
|
|
12
13
|
*
|
|
@@ -14,13 +15,27 @@
|
|
|
14
15
|
*
|
|
15
16
|
* @import { DataRule, Rule as FRule, Sequence as FSequence } from '../types.ts'
|
|
16
17
|
* @import { StringMap } from '../../types/object/types.ts'
|
|
17
|
-
* @import {
|
|
18
|
+
* @import { StringSet } from '../../types/string_set/types.ts'
|
|
19
|
+
* @import { EmptyTag, Repeat, Rule, RuleSet, Sequence, Variant } from './types.ts'
|
|
18
20
|
*/
|
|
19
21
|
|
|
20
22
|
import { stringToCodePointList } from '../../text/utf16/module.f.mjs'
|
|
21
23
|
import { map, toArray } from '../../types/list/module.f.mjs'
|
|
22
24
|
import { oneEncode } from '../module.f.mjs'
|
|
23
|
-
import { definedEntries } from '../../types/object/module.f.mjs'
|
|
25
|
+
import { definedEntries, definedValues } from '../../types/object/module.f.mjs'
|
|
26
|
+
import { contains, set } from '../../types/string_set/module.f.mjs'
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Whether a data rule is a {@link Repeat}.
|
|
30
|
+
*
|
|
31
|
+
* Every rule-dispatch site asks this instead of testing for a string inline, so
|
|
32
|
+
* the repetition rule kind has exactly one discriminator to move when the
|
|
33
|
+
* surrounding rule model changes.
|
|
34
|
+
*
|
|
35
|
+
* @param {Rule} rule
|
|
36
|
+
* @returns {rule is Repeat}
|
|
37
|
+
*/
|
|
38
|
+
export const isRepeat = rule => typeof rule === 'string'
|
|
24
39
|
|
|
25
40
|
/** @typedef {StringMap<EmptyTag>} _EmptyTagMap */
|
|
26
41
|
|
|
@@ -30,6 +45,10 @@ const emptyTagOf = map => rule => {
|
|
|
30
45
|
return undefined
|
|
31
46
|
} else if (rule instanceof Array) {
|
|
32
47
|
return rule.every(item => map[item] !== undefined) ? true : undefined
|
|
48
|
+
} else if (isRepeat(rule)) {
|
|
49
|
+
// Zero repetitions is a match whatever the body does, and it carries no
|
|
50
|
+
// tag of its own — a repetition is a sequence of items, not a choice.
|
|
51
|
+
return true
|
|
33
52
|
} else {
|
|
34
53
|
/** @type {EmptyTag} */
|
|
35
54
|
let tag = undefined
|
|
@@ -176,6 +195,92 @@ const toDataAdd = map => fr => {
|
|
|
176
195
|
return [map2, { ...set, [id]: rule }, id]
|
|
177
196
|
}
|
|
178
197
|
|
|
198
|
+
//
|
|
199
|
+
|
|
200
|
+
const { fromEntries } = Object
|
|
201
|
+
|
|
202
|
+
/** @type {(rule: Rule) => Sequence} */
|
|
203
|
+
const refs = rule => {
|
|
204
|
+
if (typeof rule === 'number') {
|
|
205
|
+
return []
|
|
206
|
+
} else if (rule instanceof Array) {
|
|
207
|
+
return rule
|
|
208
|
+
} else if (isRepeat(rule)) {
|
|
209
|
+
return [rule]
|
|
210
|
+
} else {
|
|
211
|
+
return definedValues(rule)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** @type {(ruleSet: RuleSet) => (visited: StringSet, name: string) => StringSet} */
|
|
216
|
+
const visit = ruleSet => (visited, name) =>
|
|
217
|
+
contains(name)(visited)
|
|
218
|
+
? visited
|
|
219
|
+
: refs(ruleSet[name]).reduce(visit(ruleSet), set(name)(visited))
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Every rule the entry rule can reach, itself included.
|
|
223
|
+
*
|
|
224
|
+
* @type {(ruleSet: RuleSet) => (entry: string) => StringSet}
|
|
225
|
+
*/
|
|
226
|
+
const reachable = ruleSet => entry => visit(ruleSet)(null, entry)
|
|
227
|
+
|
|
228
|
+
/** @type {(rule: Rule) => boolean} */
|
|
229
|
+
const isNone = rule => rule instanceof Array && rule.length === 0
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* The {@link Repeat} — the name of the item to repeat — that `name` encodes as
|
|
233
|
+
* right-recursion, or `undefined` when it encodes something else.
|
|
234
|
+
*
|
|
235
|
+
* The shape recognized is exactly the unambiguous 0-or-more list: a two-branch
|
|
236
|
+
* variant whose branches are an empty sequence and `[item, name]`, in either
|
|
237
|
+
* order. Everything looser stays as it is — an operator-style tree, a separated
|
|
238
|
+
* list, or a 1-or-more chain all reach `name` again, but their extra items make
|
|
239
|
+
* the grouping a real choice rather than a list, so folding them into a flat
|
|
240
|
+
* sequence would discard structure no rule name is left to recover.
|
|
241
|
+
*
|
|
242
|
+
* Two further conditions keep the fold sound rather than merely plausible:
|
|
243
|
+
* `item` must not lead back to `name`, so `name`'s only self-reference is the
|
|
244
|
+
* tail one, and `item` must not match empty, since a body that can consume
|
|
245
|
+
* nothing has infinitely many parses of the same input.
|
|
246
|
+
*
|
|
247
|
+
* @type {(ruleSet: RuleSet, emptyTags: _EmptyTagMap) => (name: string) => Repeat | undefined}
|
|
248
|
+
*/
|
|
249
|
+
const repeatOf = (ruleSet, emptyTags) => name => {
|
|
250
|
+
const rule = ruleSet[name]
|
|
251
|
+
if (typeof rule === 'number' || rule instanceof Array || isRepeat(rule)) { return undefined }
|
|
252
|
+
const branches = definedValues(rule)
|
|
253
|
+
if (branches.length !== 2) { return undefined }
|
|
254
|
+
const [a, b] = branches
|
|
255
|
+
const step = isNone(ruleSet[a]) ? b : isNone(ruleSet[b]) ? a : undefined
|
|
256
|
+
if (step === undefined) { return undefined }
|
|
257
|
+
const stepRule = ruleSet[step]
|
|
258
|
+
if (!(stepRule instanceof Array) || stepRule.length !== 2 || stepRule[1] !== name) { return undefined }
|
|
259
|
+
const [item] = stepRule
|
|
260
|
+
if (emptyTags[item] !== undefined || contains(name)(reachable(ruleSet)(item))) { return undefined }
|
|
261
|
+
return item
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Rewrites every right-recursive 0-or-more rule of a {@link RuleSet} into a
|
|
266
|
+
* {@link Repeat}, and drops the rules that the rewrite orphans.
|
|
267
|
+
*
|
|
268
|
+
* The dropped rules are the recursive branch and, unless something else still
|
|
269
|
+
* refers to it, the empty one: both are reachable only through the variant that
|
|
270
|
+
* the {@link Repeat} replaces. Pruning them keeps the invariant that every rule
|
|
271
|
+
* of a generated set is reachable from its entry, so the serialized form holds
|
|
272
|
+
* no dead grammar.
|
|
273
|
+
*
|
|
274
|
+
* @type {(ruleSet: RuleSet, entry: string) => RuleSet}
|
|
275
|
+
*/
|
|
276
|
+
export const detectRepeat = (ruleSet, entry) => {
|
|
277
|
+
const repeat = repeatOf(ruleSet, emptyTagMap(ruleSet))
|
|
278
|
+
/** @type {RuleSet} */
|
|
279
|
+
const next = fromEntries(entries(ruleSet).map(([k, v]) => [k, repeat(k) ?? v]))
|
|
280
|
+
const live = reachable(next)(entry)
|
|
281
|
+
return fromEntries(entries(next).filter(([k]) => contains(k)(live)))
|
|
282
|
+
}
|
|
283
|
+
|
|
179
284
|
/**
|
|
180
285
|
* Converts a functional grammar rule into serializable BNF data and returns
|
|
181
286
|
* the generated rule set with the entry rule identifier.
|
|
@@ -184,5 +289,5 @@ const toDataAdd = map => fr => {
|
|
|
184
289
|
*/
|
|
185
290
|
export const toData = fr => {
|
|
186
291
|
const [, ruleSet, id] = toDataAdd({})(fr)
|
|
187
|
-
return [ruleSet, id]
|
|
292
|
+
return [detectRepeat(ruleSet, id), id]
|
|
188
293
|
}
|
package/fjs/bnf/data/proof.f.mjs
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
import { identity } from '../../types/function/module.f.mjs'
|
|
8
8
|
import { sort } from '../../types/object/module.f.mjs'
|
|
9
|
-
import { oneEncode, option, range, rangeDecode, repeat0Plus, set } from '../module.f.mjs'
|
|
10
|
-
import { emptyTagMap, toData } from './module.f.mjs'
|
|
9
|
+
import { none, oneEncode, option, range, rangeDecode, repeat0Plus, set } from '../module.f.mjs'
|
|
10
|
+
import { detectRepeat, emptyTagMap, isRepeat, toData } from './module.f.mjs'
|
|
11
11
|
import { assertEq } from '../../asserts/module.f.mjs'
|
|
12
12
|
import { stringify } from '../../media/json/module.f.mjs'
|
|
13
13
|
import { classic, deterministic } from '../testlib.f.mjs'
|
|
@@ -88,11 +88,82 @@ export const proof = {
|
|
|
88
88
|
if (result !== '[{"0":{"some":"1","none":"3"},"1":["2"],"2":1627390049,"3":[],"":["0","r"],"r":{"some":"","none":"3"}},"r"]') { throw result }
|
|
89
89
|
},
|
|
90
90
|
() => {
|
|
91
|
+
// The item is not nullable, so this one folds into a `repeat`, and
|
|
92
|
+
// the recursive and empty branches it replaced are pruned.
|
|
91
93
|
const repeatRule = repeat0Plus(set(' \n\r\t'))
|
|
92
94
|
const result = stringify(identity)(toData(repeatRule))
|
|
93
|
-
|
|
95
|
+
const expected = '[{"0":{" ":"1","\\n":"2","\\r":"3","\\t":"4"},"1":536870944,"2":167772170,"3":218103821,"4":150994953,"r":"0"},"r"]'
|
|
96
|
+
assertEq(result, expected, [result, expected])
|
|
94
97
|
}
|
|
95
98
|
],
|
|
99
|
+
isRepeat: () => {
|
|
100
|
+
assertEq(isRepeat('a'), true)
|
|
101
|
+
// Nothing else in a rule set is a string, so the four rule kinds are
|
|
102
|
+
// told apart by JavaScript type alone — no shape has to be probed.
|
|
103
|
+
assertEq(isRepeat(['a', 'b']), false)
|
|
104
|
+
assertEq(isRepeat({ some: 'a' }), false)
|
|
105
|
+
assertEq(isRepeat(oneEncode(0x41)), false)
|
|
106
|
+
},
|
|
107
|
+
detectRepeat: [
|
|
108
|
+
() => {
|
|
109
|
+
const result = stringify(identity)(toData(repeat0Plus(range('AF'))))
|
|
110
|
+
const expected = '[{"0":1090519110,"r":"0"},"r"]'
|
|
111
|
+
assertEq(result, expected, [result, expected])
|
|
112
|
+
},
|
|
113
|
+
() => {
|
|
114
|
+
// Nothing about the fold depends on `repeat0Plus`: a hand-written
|
|
115
|
+
// 0-or-more list is the same shape and folds the same way. `classic`
|
|
116
|
+
// spells one out as `characters`.
|
|
117
|
+
const [ruleSet] = toData(classic())
|
|
118
|
+
const repeats = Object.entries(ruleSet).filter(([, v]) => isRepeat(v))
|
|
119
|
+
assertEq(JSON.stringify(repeats), '[["characters","character"]]')
|
|
120
|
+
},
|
|
121
|
+
() => {
|
|
122
|
+
// Already folded: a `repeat` is not itself a variant to fold again.
|
|
123
|
+
const [ruleSet, entry] = toData(repeat0Plus(range('AF')))
|
|
124
|
+
assertEq(
|
|
125
|
+
stringify(identity)(detectRepeat(ruleSet, entry)),
|
|
126
|
+
stringify(identity)(ruleSet))
|
|
127
|
+
},
|
|
128
|
+
() => {
|
|
129
|
+
// 1-or-more: the base branch is the item, not the empty rule.
|
|
130
|
+
const digit = range('09')
|
|
131
|
+
const digits = () => ({ digit, digits: [digit, digits] })
|
|
132
|
+
const result = stringify(identity)(toData(digits))
|
|
133
|
+
const expected = '[{"0":["","digits"],"":805306425,"digits":{"digit":"","digits":"0"}},"digits"]'
|
|
134
|
+
assertEq(result, expected, [result, expected])
|
|
135
|
+
},
|
|
136
|
+
() => {
|
|
137
|
+
// A separated list repeats `[item, separator, self]`; which of the
|
|
138
|
+
// three items are the list's elements is not something the shape
|
|
139
|
+
// says, so it stays a variant.
|
|
140
|
+
const sep = () => ({ none, list: [range('09'), ',', sep] })
|
|
141
|
+
const result = stringify(identity)(toData(sep))
|
|
142
|
+
const expected = '[{"0":["1","2","sep"],"1":805306425,"2":["3"],"3":738197548,"":[],"sep":{"none":"","list":"0"}},"sep"]'
|
|
143
|
+
assertEq(result, expected, [result, expected])
|
|
144
|
+
},
|
|
145
|
+
() => {
|
|
146
|
+
// The tail is not the rule's only self-reference: the item leads back
|
|
147
|
+
// into it, so the recursion is a tree rather than a list.
|
|
148
|
+
const nested = () => ({ none, some: [inner, nested] })
|
|
149
|
+
const inner = () => ({ leaf: range('AA'), group: ['(', nested, ')'] })
|
|
150
|
+
const [ruleSet] = toData(nested)
|
|
151
|
+
assertEq(JSON.stringify(ruleSet.nested), '{"none":"","some":"0"}')
|
|
152
|
+
},
|
|
153
|
+
() => {
|
|
154
|
+
// A nullable item gives the same input infinitely many parses, so
|
|
155
|
+
// the repetition is not the unambiguous one this fold is limited to.
|
|
156
|
+
const [ruleSet, entry] = toData(repeat0Plus(option('a')))
|
|
157
|
+
assertEq(JSON.stringify(ruleSet[entry]), '{"some":"","none":"3"}')
|
|
158
|
+
},
|
|
159
|
+
() => {
|
|
160
|
+
// Two branches, an empty one and a terminal: the non-empty branch is
|
|
161
|
+
// not a sequence at all.
|
|
162
|
+
const result = stringify(identity)(toData({ e: '', a: range('AA') }))
|
|
163
|
+
const expected = '[{"0":[],"1":1090519105,"":{"e":"0","a":"1"}},""]'
|
|
164
|
+
assertEq(result, expected, [result, expected])
|
|
165
|
+
},
|
|
166
|
+
],
|
|
96
167
|
variantTest: () => {
|
|
97
168
|
const varintRule = { a: 'a', b: 'b'}
|
|
98
169
|
const result = stringify(sort)(toData(varintRule))
|
package/fjs/bnf/data/types.d.ts
CHANGED
|
@@ -16,15 +16,33 @@ export type TerminalRange = number;
|
|
|
16
16
|
export type Sequence = readonly string[];
|
|
17
17
|
/** A variant of rule names. */
|
|
18
18
|
export type Variant = StringMap<string>;
|
|
19
|
+
/**
|
|
20
|
+
* Zero or more repetitions of the named rule.
|
|
21
|
+
*
|
|
22
|
+
* The functional grammar has no repetition primitive — `repeat0Plus(x)` expands
|
|
23
|
+
* to a right-recursive variant — so this is the one rule kind `toData` derives
|
|
24
|
+
* rather than transcribes: it recognizes the unambiguous 0-or-more shape and
|
|
25
|
+
* emits this instead, which is what lets a parser backend match the body
|
|
26
|
+
* iteratively and produce one flat node for the whole repetition.
|
|
27
|
+
*
|
|
28
|
+
* A bare rule name keeps the four rule kinds disjoint by JavaScript type alone:
|
|
29
|
+
* a number is a {@link TerminalRange}, an array a {@link Sequence}, an object a
|
|
30
|
+
* {@link Variant}, and a string this. Nothing else in a {@link RuleSet} is a
|
|
31
|
+
* string, so no shape has to be probed to tell one kind from another. (The
|
|
32
|
+
* *functional* `DataRule` does have a string case — a Unicode literal — but
|
|
33
|
+
* that one never reaches here: `toData` expands it to terminals.)
|
|
34
|
+
*/
|
|
35
|
+
export type Repeat = string;
|
|
19
36
|
/**
|
|
20
37
|
* Grammar rule definition.
|
|
21
38
|
*
|
|
22
39
|
* It can be one of:
|
|
23
40
|
* - a tagged variant map,
|
|
24
41
|
* - a sequence of referenced rule names,
|
|
25
|
-
* - an encoded terminal range
|
|
42
|
+
* - an encoded terminal range,
|
|
43
|
+
* - the name of a rule to repeat zero or more times.
|
|
26
44
|
*/
|
|
27
|
-
export type Rule = Variant | Sequence | TerminalRange;
|
|
45
|
+
export type Rule = Variant | Sequence | TerminalRange | Repeat;
|
|
28
46
|
/** The full grammar */
|
|
29
47
|
export type RuleSet = Readonly<Record<string, Rule>>;
|
|
30
48
|
/**
|
|
@@ -4,39 +4,53 @@
|
|
|
4
4
|
* Built from the serializable IR in `fjs/bnf/data`, this is a sibling of the
|
|
5
5
|
* LL(1) dispatch builder (`fjs/bnf/ll1`). It walks the grammar by recursive
|
|
6
6
|
* descent and preserves per-code-point metadata, producing a metadata-aware
|
|
7
|
-
* AST
|
|
8
|
-
*
|
|
7
|
+
* AST over leaves that carry it — `Ast<CodePointMeta<T>>`, built with the
|
|
8
|
+
* shared layer in `fjs/bnf/matcher`. Nullability (which rule can match empty
|
|
9
|
+
* input) is computed once by {@link emptyTagMap} in `fjs/bnf/data`.
|
|
9
10
|
*
|
|
10
11
|
* The caller passes physical symbols only; the matcher synthesizes the one
|
|
11
|
-
* logical EOF
|
|
12
|
-
*
|
|
12
|
+
* logical EOF after them, so a grammar can require the end of input with the
|
|
13
|
+
* `eof` terminal.
|
|
13
14
|
*
|
|
14
15
|
* A failed result also carries a {@link DescentFailure}: the furthest position a
|
|
15
16
|
* terminal was rejected at, which — unlike the result's own index — never
|
|
16
17
|
* rewinds and is what diagnostics should be built from.
|
|
17
18
|
*
|
|
19
|
+
* A `Repeat` rule is matched iteratively rather than by descending into itself
|
|
20
|
+
* once per item, so a repetition produces one AST node holding a flat sequence
|
|
21
|
+
* of the items it matched instead of the right-recursive chain its functional
|
|
22
|
+
* spelling would otherwise build.
|
|
23
|
+
*
|
|
18
24
|
* See `./types.ts` for the type-level API.
|
|
19
25
|
*
|
|
20
26
|
* @module
|
|
21
27
|
*
|
|
22
28
|
* @import { TerminalRange } from '../types.ts'
|
|
23
|
-
* @import { Rule as DataRule, Sequence } from '../data/types.ts'
|
|
29
|
+
* @import { Rule as DataRule, RuleSet, Sequence } from '../data/types.ts'
|
|
24
30
|
* @import { Rule as FRule } from '../types.ts'
|
|
25
|
-
* @import {
|
|
31
|
+
* @import { List } from '../../types/list/types.ts'
|
|
32
|
+
* @import { Ast, AstResult, AstSequence, AstTag, Cursor } from '../matcher/types.ts'
|
|
33
|
+
* @import { CodePointMeta, DescentFailure, DescentMatch, DescentMatchResult, DescentMatchRule } from './types.ts'
|
|
26
34
|
*/
|
|
27
35
|
import type { TerminalRange } from '../types.ts';
|
|
36
|
+
import type { RuleSet } from '../data/types.ts';
|
|
28
37
|
import type { Rule as FRule } from '../types.ts';
|
|
29
|
-
import type {
|
|
30
|
-
|
|
38
|
+
import type { AstResult, Cursor } from '../matcher/types.ts';
|
|
39
|
+
import type { CodePointMeta, DescentMatch } from './types.ts';
|
|
31
40
|
export type _Failure = {
|
|
32
|
-
readonly pos:
|
|
41
|
+
readonly pos: Cursor;
|
|
33
42
|
readonly expected: readonly TerminalRange[];
|
|
34
43
|
};
|
|
35
|
-
export type _Result<T> =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
export type _Result<T> = AstResult<CodePointMeta<T>, Cursor>;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a recursive descent parser from an already materialized
|
|
47
|
+
* {@link RuleSet}, preserving metadata for each consumed code point.
|
|
48
|
+
*
|
|
49
|
+
* @template T
|
|
50
|
+
* @param {RuleSet} ruleSet
|
|
51
|
+
* @returns {DescentMatch<T>}
|
|
52
|
+
*/
|
|
53
|
+
export declare const descentParserRuleSet: <T>(ruleSet: RuleSet) => DescentMatch<T>;
|
|
40
54
|
/**
|
|
41
55
|
* Creates a recursive descent parser that preserves metadata for each consumed
|
|
42
56
|
* code point.
|