effect-grammar 0.3.0 → 0.5.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 +168 -229
- package/dist/binary.d.ts +64 -0
- package/dist/binary.d.ts.map +1 -0
- package/dist/binary.js +246 -0
- package/dist/binary.js.map +1 -0
- package/dist/codec.d.ts +17 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +14 -0
- package/dist/codec.js.map +1 -0
- package/dist/combinators.d.ts +112 -6
- package/dist/combinators.d.ts.map +1 -1
- package/dist/combinators.js +218 -28
- package/dist/combinators.js.map +1 -1
- package/dist/compile.d.ts +36 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +228 -0
- package/dist/compile.js.map +1 -0
- package/dist/core.d.ts +34 -1
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js +51 -1
- package/dist/core.js.map +1 -1
- package/dist/env.d.ts +5 -0
- package/dist/env.d.ts.map +1 -1
- package/dist/env.js +20 -1
- package/dist/env.js.map +1 -1
- package/dist/errors.d.ts +12 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +40 -4
- package/dist/errors.js.map +1 -1
- package/dist/gen.d.ts.map +1 -1
- package/dist/gen.js +5 -7
- package/dist/gen.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -3
- package/dist/index.js.map +1 -1
- package/dist/parse.d.ts +10 -1
- package/dist/parse.d.ts.map +1 -1
- package/dist/parse.js +81 -19
- package/dist/parse.js.map +1 -1
- package/dist/pattern.d.ts +2 -1
- package/dist/pattern.d.ts.map +1 -1
- package/dist/pattern.js +46 -2
- package/dist/pattern.js.map +1 -1
- package/dist/print.d.ts +8 -0
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +136 -18
- package/dist/print.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +33 -6
- package/dist/render.js.map +1 -1
- package/dist/schema.d.ts +8 -3
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +11 -13
- package/dist/schema.js.map +1 -1
- package/dist/testing.d.ts +18 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +72 -0
- package/dist/testing.js.map +1 -0
- package/package.json +9 -3
- package/dist/grammar.d.ts +0 -8
- package/dist/grammar.d.ts.map +0 -1
- package/dist/grammar.js +0 -8
- package/dist/grammar.js.map +0 -1
- package/dist/recovery.d.ts +0 -10
- package/dist/recovery.d.ts.map +0 -1
- package/dist/recovery.js +0 -296
- package/dist/recovery.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,255 +1,194 @@
|
|
|
1
1
|
# effect-grammar
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
inside your strings: connection strings, duration strings (`1h30m`), cron
|
|
7
|
-
expressions, search queries, or DSLs.
|
|
8
|
-
|
|
9
|
-
You write the grammar definition once. You get four outputs:
|
|
10
|
-
|
|
11
|
-
- **A parser.** Reads text and outputs structured data, with line and column
|
|
12
|
-
error messages.
|
|
13
|
-
- **A printer.** Converts structured data back to canonical text.
|
|
14
|
-
- **A `Schema.Codec<A, string>`.** Decoding parses, encoding prints, and Schema
|
|
15
|
-
refinements compose.
|
|
16
|
-
- **A text renderer.** Formats the grammar as readable text, for documentation
|
|
17
|
-
and schema descriptions.
|
|
18
|
-
|
|
19
|
-
## Why effect-grammar?
|
|
20
|
-
|
|
21
|
-
`Schema.transformOrFail` makes you write and maintain both directions by hand.
|
|
22
|
-
`Schema.TemplateLiteralParser` only handles flat `${a}-${b}` patterns.
|
|
23
|
-
`effect-grammar` derives both directions for you. It handles optional parts,
|
|
24
|
-
repetition, alternation, recursion, and parts that depend on earlier parts.
|
|
25
|
-
|
|
26
|
-
This is for format strings, not documents. The parser backtracks. There is no
|
|
27
|
-
memoization and no left recursion. Printing is canonical, not pretty.
|
|
3
|
+
Write a grammar once, then use it to parse text, print values, or derive an
|
|
4
|
+
Effect Schema codec. Grammars are bidirectional by design, with explicit
|
|
5
|
+
round-trip checking when you need an invertibility guarantee.
|
|
28
6
|
|
|
29
7
|
## Install
|
|
30
8
|
|
|
31
|
-
|
|
32
|
-
pnpm add effect-grammar
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Requires Effect `4.0.0-rc.112` or newer in the Effect 4 line.
|
|
36
|
-
|
|
37
|
-
## A grammar
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import * as Grammar from "effect-grammar"
|
|
41
|
-
|
|
42
|
-
const endpoint = Grammar.gen(function* () {
|
|
43
|
-
yield* Grammar.literal("https://")
|
|
44
|
-
const host = yield* Grammar.regex(/[^:/?#]+/, "host")
|
|
45
|
-
const port = yield* Grammar.optional(Grammar.prefix(":", Grammar.integer))
|
|
46
|
-
return { host, port }
|
|
47
|
-
})
|
|
48
|
-
|
|
49
|
-
Grammar.parse(endpoint, "https://effect.website:8080")
|
|
50
|
-
// Result.succeed({ host: "effect.website", port: 8080 })
|
|
51
|
-
|
|
52
|
-
Grammar.parse(endpoint, "https://effect.website:abc")
|
|
53
|
-
// Result.fail(ParseError: line 1, column 24: expected integer, found "a")
|
|
54
|
-
|
|
55
|
-
Grammar.print(endpoint, { host: "effect.website", port: 443 })
|
|
56
|
-
// Result.succeed("https://effect.website:443")
|
|
9
|
+
`effect` is a peer dependency:
|
|
57
10
|
|
|
58
|
-
|
|
59
|
-
|
|
11
|
+
```sh
|
|
12
|
+
npm install effect effect-grammar
|
|
60
13
|
```
|
|
61
14
|
|
|
62
|
-
|
|
63
|
-
yet.
|
|
64
|
-
|
|
65
|
-
Inside the generator, `yield*` on a value grammar does not give you a value. It
|
|
66
|
-
gives you a `Ref<A>`: a stand-in for the value that will exist at parse and
|
|
67
|
-
print time. `yield*` on a silent grammar, such as `literal` or `symbol`, gives
|
|
68
|
-
nothing back. Silent grammars carry no value.
|
|
69
|
-
|
|
70
|
-
The generator's return value is a pattern over those refs. It can be a bare ref,
|
|
71
|
-
an object, an array, or constants around them. Parsing fills the pattern in.
|
|
72
|
-
Printing reads each ref back out of the value you hand it. That is what makes
|
|
73
|
-
the grammar invertible.
|
|
15
|
+
## Quick start
|
|
74
16
|
|
|
75
17
|
```ts
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## The one rule
|
|
86
|
-
|
|
87
|
-
Every binding must appear in the return pattern exactly once. `gen` throws at
|
|
88
|
-
build time if one does not. A binding the return does not hold is a value the
|
|
89
|
-
printer could not print.
|
|
90
|
-
|
|
91
|
-
To parse something and then drop it, wrap it with `skip`. The grammar prints its
|
|
92
|
-
canonical form instead.
|
|
93
|
-
|
|
94
|
-
## Refs are not values
|
|
95
|
-
|
|
96
|
-
A ref is a stand-in, so JavaScript cannot look inside it. Comparing a ref with
|
|
97
|
-
`===` is a type error. Interpolating one into a string throws.
|
|
98
|
-
|
|
99
|
-
Branching on a ref is `match`, described below. For everything else there are
|
|
100
|
-
small helpers: `defaulted` supplies a value when a part is absent.
|
|
101
|
-
`get(ref, key)` reads a property of a ref, including reserved names such as
|
|
102
|
-
`then` and `toJSON`.
|
|
103
|
-
|
|
104
|
-
## Depending on an earlier part
|
|
105
|
-
|
|
106
|
-
`match` picks a grammar based on an earlier binding. The printer runs the same
|
|
107
|
-
choice backwards.
|
|
108
|
-
|
|
109
|
-
```ts
|
|
110
|
-
const tagged = Grammar.gen(function* () {
|
|
111
|
-
const kind = yield* Grammar.choice(
|
|
112
|
-
Grammar.literal("n:").pipe(Grammar.as("num")),
|
|
113
|
-
Grammar.literal("w:").pipe(Grammar.as("word")),
|
|
114
|
-
)
|
|
115
|
-
const value = yield* Grammar.match(kind, {
|
|
116
|
-
num: Grammar.integer,
|
|
117
|
-
word: Grammar.regex(/[a-z]+/, "word"),
|
|
118
|
-
})
|
|
119
|
-
return { kind, value }
|
|
120
|
-
})
|
|
121
|
-
// parses "n:12" → { kind: "num", value: 12 }
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
`match` requires the cases to cover every literal of the selector's type.
|
|
125
|
-
`matchValue` is the same idea with number or mixed literal keys. Keys keep their
|
|
126
|
-
type, so `1` and `"1"` are distinct cases.
|
|
127
|
-
|
|
128
|
-
A property of a ref is a ref to that property. A header parsed by one grammar
|
|
129
|
-
can steer the body of another:
|
|
130
|
-
|
|
131
|
-
```ts
|
|
132
|
-
const frame = Grammar.gen(function* () {
|
|
133
|
-
const header = yield* headerGrammar // { kind: "text" | "bin"; size: number }
|
|
134
|
-
yield* Grammar.literal(":")
|
|
135
|
-
const body = yield* Grammar.match(header.kind, {
|
|
136
|
-
text: Grammar.take(header.size),
|
|
137
|
-
bin: Grammar.repeat(Grammar.regex(/[01]/, "bit"), header.size),
|
|
138
|
-
})
|
|
139
|
-
return { header, body }
|
|
18
|
+
import { Schema } from "effect"
|
|
19
|
+
import * as G from "effect-grammar"
|
|
20
|
+
|
|
21
|
+
const endpoint = G.gen(function* () {
|
|
22
|
+
yield* G.literal("https://")
|
|
23
|
+
const host = yield* G.regex(/[^:/?#]+/, "host")
|
|
24
|
+
yield* G.literal(":")
|
|
25
|
+
const port = yield* G.integer
|
|
26
|
+
return { host, port }
|
|
140
27
|
})
|
|
141
|
-
```
|
|
142
28
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
const netstring = Grammar.gen(function* () {
|
|
149
|
-
const length = yield* Grammar.integer
|
|
150
|
-
yield* Grammar.literal(":")
|
|
151
|
-
const payload = yield* Grammar.take(length)
|
|
152
|
-
yield* Grammar.literal(",")
|
|
153
|
-
return { length, payload }
|
|
154
|
-
})
|
|
155
|
-
// "5:hello," ⇄ { length: 5, payload: "hello" }
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
`seq(...silents)` builds a silent sequence.
|
|
159
|
-
|
|
160
|
-
## Values and alternatives
|
|
161
|
-
|
|
162
|
-
`transform` maps between two types, in both directions. Write plain functions.
|
|
163
|
-
If a callback throws, the grammar fails cleanly. Use `transformOrFail` when a
|
|
164
|
-
direction should return a `Result` instead:
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
const jsonString = Grammar.regex(/"(?:[^"\\]|\\.)*"/, "string").pipe(
|
|
168
|
-
Grammar.transformOrFail({
|
|
169
|
-
decode: (text) =>
|
|
170
|
-
Result.try({
|
|
171
|
-
try: () => JSON.parse(text),
|
|
172
|
-
catch: (error) => ({ message: String(error) }),
|
|
173
|
-
}),
|
|
174
|
-
encode: (value) => Result.succeed(JSON.stringify(value)),
|
|
29
|
+
const Endpoint = G.codec(
|
|
30
|
+
endpoint,
|
|
31
|
+
Schema.Struct({
|
|
32
|
+
host: Schema.NonEmptyString,
|
|
33
|
+
port: Schema.Int,
|
|
175
34
|
}),
|
|
176
35
|
)
|
|
177
|
-
```
|
|
178
36
|
|
|
179
|
-
|
|
180
|
-
|
|
37
|
+
Schema.decodeSync(Endpoint)("https://effect.website:443")
|
|
38
|
+
// { host: "effect.website", port: 443 }
|
|
181
39
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
earlier branch. `taggedChoice` records the chosen branch in the result.
|
|
185
|
-
|
|
186
|
-
```ts
|
|
187
|
-
const value = Grammar.taggedChoice("kind", {
|
|
188
|
-
number: Grammar.integer,
|
|
189
|
-
word: Grammar.regex(/[a-z]+/, "word"),
|
|
190
|
-
})
|
|
191
|
-
// parses "12" → { kind: "number", value: 12 }
|
|
192
|
-
// prints the same → "12"
|
|
40
|
+
Schema.encodeSync(Endpoint)({ host: "effect.website", port: 443 })
|
|
41
|
+
// "https://effect.website:443"
|
|
193
42
|
```
|
|
194
43
|
|
|
195
|
-
|
|
44
|
+
## Operations
|
|
45
|
+
|
|
46
|
+
- `parse(grammar, text)` interprets from cursor zero and succeeds only after
|
|
47
|
+
consuming the whole string.
|
|
48
|
+
- `print(grammar, value)` runs the grammar's printer. It validates local printer
|
|
49
|
+
requirements but does not verify that the result parses back to the same
|
|
50
|
+
value.
|
|
51
|
+
- `printChecked(grammar, value)` prints, reparses the whole output, and compares
|
|
52
|
+
the result with Effect's equality. It fails when that value round trip does
|
|
53
|
+
not hold.
|
|
54
|
+
- `prepare(grammar)` runs the library's static validation once, then returns
|
|
55
|
+
`parse`, `print`, and `printChecked` functions bound to the grammar, plus its
|
|
56
|
+
rendering and fidelity audit. It is not compilation or optimization; runtime
|
|
57
|
+
failures remain possible.
|
|
58
|
+
- `codec(grammar, schema)` creates an Effect Schema codec. Encoding uses checked
|
|
59
|
+
printing by default; `{ roundTrip: "off" }` selects unchecked printing.
|
|
60
|
+
|
|
61
|
+
`regex(re, name)` stores the source and flags without `g` or `y`. During parsing
|
|
62
|
+
it uses a fresh sticky matcher at the current cursor, so `^`, `$`, lookarounds,
|
|
63
|
+
and flags retain JavaScript `RegExp` semantics relative to the original full
|
|
64
|
+
input. Printing requires the value itself to be a string matched in full. The
|
|
65
|
+
caller's `RegExp` and `lastIndex` are not mutated.
|
|
66
|
+
|
|
67
|
+
## Composition and correctness
|
|
68
|
+
|
|
69
|
+
`choice` parses branches in order and prints with the first printer that accepts
|
|
70
|
+
the value. `checkedChoice` uses the first accepting branch whose produced text
|
|
71
|
+
reparses to an equal value. `choiceOn(tag, cases)` instead dispatches printing
|
|
72
|
+
from an existing discriminant field; parsing is still ordered and can remain
|
|
73
|
+
ambiguous. `taggedChoice(tag, cases)` wraps each branch's value as
|
|
74
|
+
`{ [tag]: key, value }` and dispatches by that generated tag.
|
|
75
|
+
`literals(...strings)` is a choice of strings whose value is the string that
|
|
76
|
+
matched. It tries longer strings first, so `literals(">", ">=")` still reads
|
|
77
|
+
`>=`.
|
|
78
|
+
|
|
79
|
+
For explicit branch order or number/boolean discriminants, use
|
|
80
|
+
`choiceOnEntries(tag, entries)` with an array of `[key, grammar]` entries.
|
|
81
|
+
`choiceOn` and `taggedChoice` reject JavaScript array-index keys because object
|
|
82
|
+
enumeration can reorder them.
|
|
83
|
+
|
|
84
|
+
`gen` result objects are exact printer patterns: printing rejects missing,
|
|
85
|
+
extra, symbol, or otherwise unexpected own keys. Arrays must have exactly the
|
|
86
|
+
expected length.
|
|
87
|
+
|
|
88
|
+
`many`, `sepBy`, and exact `repeat` require every successfully parsed item to
|
|
89
|
+
advance the cursor; zero-width items fail rather than loop. `validate`/`prepare`
|
|
90
|
+
report repetitions whose item can be proved to match empty input, but validation
|
|
91
|
+
is intentionally not a proof of all behavior.
|
|
92
|
+
|
|
93
|
+
`take(count)` reads a fixed number of UTF-16 code units and
|
|
94
|
+
`repeat(item, count)` a fixed number of items; the count is a number or a ref
|
|
95
|
+
bound earlier in the same `gen`. `lengthPrefixed(length)` and
|
|
96
|
+
`countPrefixed(item, count)` parse a prefix and then that many UTF-16 code units
|
|
97
|
+
or items, and derive the prefix from the value when printing, so the value does
|
|
98
|
+
not carry it. `filter(predicate, name)` keeps a grammar's value only when the
|
|
99
|
+
predicate accepts it, in both directions.
|
|
100
|
+
|
|
101
|
+
`merge(...parts)` sequences grammars that produce objects and flattens their
|
|
102
|
+
fields into one object. Each part must have statically known fields: a `struct`,
|
|
103
|
+
a `gen` that returns an object, another `merge`, `Binary.bits`, or a `filter`
|
|
104
|
+
over any of these. A general transform is rejected, because its fields cannot be
|
|
105
|
+
known. Printing splits the value by those fields, so errors keep flat paths.
|
|
106
|
+
Duplicate fields are rejected on construction.
|
|
107
|
+
|
|
108
|
+
`suspend(() => grammar)` enables recursive definitions. Its thunk is evaluated
|
|
109
|
+
lazily on first resolution and the resolved grammar is cached. Direct left
|
|
110
|
+
recursion at the same parse position and recursive printing that does not
|
|
111
|
+
consume a value are rejected; recursive grammars must be productive.
|
|
112
|
+
|
|
113
|
+
Transforms state different fidelity intentions:
|
|
114
|
+
|
|
115
|
+
- `transform` and `transformOrFail` make no inverse claim.
|
|
116
|
+
- `iso` records the author's inverse claim; the library does not prove it.
|
|
117
|
+
- `partialIso` records a fallible pair intended to agree where both directions
|
|
118
|
+
succeed.
|
|
119
|
+
- `auditFidelity` lists transforms without a full inverse claim; an empty list
|
|
120
|
+
is not proof of a round trip.
|
|
121
|
+
|
|
122
|
+
Use `printChecked` or the helpers from `effect-grammar/testing` to test the
|
|
123
|
+
values and accepted texts relevant to your grammar. These are properties to
|
|
124
|
+
verify, not laws guaranteed for every grammar.
|
|
125
|
+
|
|
126
|
+
## Binary
|
|
127
|
+
|
|
128
|
+
`effect-grammar/Binary` applies the same grammars to a `Uint8Array`. Bytes pass
|
|
129
|
+
through the engine as a binary string with one code unit per byte, so every core
|
|
130
|
+
combinator composes with the byte-oriented ones.
|
|
196
131
|
|
|
197
132
|
```ts
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
jsonNull,
|
|
202
|
-
jsonBool,
|
|
203
|
-
jsonNumber,
|
|
204
|
-
jsonString,
|
|
205
|
-
jsonArray,
|
|
206
|
-
jsonObject,
|
|
207
|
-
),
|
|
208
|
-
"value",
|
|
209
|
-
)
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
See `examples/` for JSON, Scheme, a Postgres DSN, netstrings, a wire protocol,
|
|
213
|
-
and GitHub's search syntax.
|
|
214
|
-
|
|
215
|
-
## Products, delimiters, and trivia
|
|
216
|
-
|
|
217
|
-
`struct` and `tuple` build products without a generator. Their staged form is
|
|
218
|
-
the same as what `gen` builds.
|
|
219
|
-
|
|
220
|
-
Delimiter combinators work data-first and data-last:
|
|
133
|
+
import { Schema } from "effect"
|
|
134
|
+
import * as Grammar from "effect-grammar"
|
|
135
|
+
import * as Binary from "effect-grammar/Binary"
|
|
221
136
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Grammar.
|
|
226
|
-
Grammar.defaulted(443),
|
|
137
|
+
const header = Grammar.merge(
|
|
138
|
+
Grammar.struct({ id: Binary.uint16 }),
|
|
139
|
+
Binary.bits({ qr: 1, opcode: 4, aa: 1, tc: 1, rd: 1, ra: 1, z: 3, rcode: 4 }),
|
|
140
|
+
Grammar.struct({ qdcount: Binary.uint16 }),
|
|
227
141
|
)
|
|
228
142
|
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
143
|
+
const Header = Binary.codec(
|
|
144
|
+
header,
|
|
145
|
+
Schema.Struct({
|
|
146
|
+
id: Binary.Uint16,
|
|
147
|
+
qr: Binary.Bit,
|
|
148
|
+
opcode: Binary.Uint(4),
|
|
149
|
+
aa: Binary.Bit,
|
|
150
|
+
tc: Binary.Bit,
|
|
151
|
+
rd: Binary.Bit,
|
|
152
|
+
ra: Binary.Bit,
|
|
153
|
+
z: Binary.Uint(3),
|
|
154
|
+
rcode: Binary.Uint(4),
|
|
155
|
+
qdcount: Binary.Uint16,
|
|
156
|
+
}),
|
|
232
157
|
)
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
`between` is the two-sided delimiter. `wrap` is another name for it. `lexeme`
|
|
236
|
-
skips trailing whitespace after a token, and prints none. `symbol` is a literal
|
|
237
|
-
lexeme. `space` is one exact space. `spaces` accepts one or more whitespace
|
|
238
|
-
characters and prints one canonical space.
|
|
239
|
-
|
|
240
|
-
## Rendering
|
|
241
158
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
The interpreter AST is private. Public grammars expose only `Grammar<A>`,
|
|
246
|
-
`Ref<A>`, and the combinators.
|
|
247
|
-
|
|
248
|
-
## Errors
|
|
249
|
-
|
|
250
|
-
Parse errors report the furthest position reached, with every expected form at
|
|
251
|
-
that position, plus line and column.
|
|
159
|
+
Schema.decodeSync(Header)(Uint8Array.of(0xbe, 0xef, 0x01, 0x00, 0x00, 0x01))
|
|
160
|
+
// { id: 48879, qr: 0, opcode: 0, aa: 0, tc: 0, rd: 1, ra: 0, z: 0, rcode: 0, qdcount: 1 }
|
|
161
|
+
```
|
|
252
162
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
163
|
+
- `uint8` to `uint64`, `int8` to `int64`, `float32`, and `float64` are
|
|
164
|
+
big-endian; the `le` suffix, as in `uint16le`, reads little-endian. 64-bit
|
|
165
|
+
integers are bigints. `float32` prints only numbers that single precision
|
|
166
|
+
holds exactly.
|
|
167
|
+
- `varuint` is unsigned LEB128 within the safe integer range, and `varint` its
|
|
168
|
+
zigzag-encoded signed form for integers from `-(2 ** 52)` to `2 ** 52 - 1`.
|
|
169
|
+
Parsing accepts padded encodings of any length; printing writes the shortest
|
|
170
|
+
one.
|
|
171
|
+
- `bits(layout)` splits a whole number of bytes into named fields, first field
|
|
172
|
+
highest. A one-bit field has type `0 | 1`; wider fields are numbers of up to
|
|
173
|
+
53 bits. Printing rejects a field that does not fit its width.
|
|
174
|
+
- `bytes(count)` reads a `Uint8Array` of a constant or previously bound length,
|
|
175
|
+
`lengthPrefixed(length)` derives its prefix, a byte count, when printing, and
|
|
176
|
+
`literal(...bytes)` matches a fixed sequence such as a magic number.
|
|
177
|
+
- `ascii` and `utf8` turn a `Uint8Array` grammar into a string grammar. Invalid
|
|
178
|
+
bytes fail to parse and unencodable strings fail to print; `auditFidelity`
|
|
179
|
+
lists `utf8` as partial.
|
|
180
|
+
- `Bit`, `Uint(bits)` and `Int(bits)` for 1 to 53 bits, `Uint8` to `Uint64`, and
|
|
181
|
+
`Int8` to `Int64` are schemas for the values these grammars produce, and
|
|
182
|
+
`hex(bytes)` formats a `Uint8Array` for display.
|
|
183
|
+
- `parse`, `print`, `printChecked`, and `codec` mirror the text operations over
|
|
184
|
+
`Uint8Array`. Parse failures report a byte offset and the byte found; input
|
|
185
|
+
that ends inside a fixed-width field fails at the end of the input.
|
|
186
|
+
|
|
187
|
+
A grammar that prints a character above `0xff` cannot be encoded and fails to
|
|
188
|
+
print.
|
|
189
|
+
|
|
190
|
+
## Examples
|
|
191
|
+
|
|
192
|
+
The `examples/` directory includes endpoint and connection-string grammars,
|
|
193
|
+
JSON, HTTP ranges, IP addresses, recursive Scheme syntax, contextual printing,
|
|
194
|
+
Schema error integration, and a binary DNS message codec.
|
package/dist/binary.d.ts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Result, Schema } from "effect";
|
|
2
|
+
import { type CodecOptions } from "./codec.ts";
|
|
3
|
+
import { type Grammar, type Ref, type Silent } from "./core.ts";
|
|
4
|
+
import { PrintError } from "./errors.ts";
|
|
5
|
+
export { hex } from "./errors.ts";
|
|
6
|
+
export declare const Bit: Schema.Literals<readonly [0, 1]>;
|
|
7
|
+
export declare const Uint: (size: number) => Schema.Int;
|
|
8
|
+
export declare const Int: (size: number) => Schema.Int;
|
|
9
|
+
export declare const Uint8: Schema.Int;
|
|
10
|
+
export declare const Uint16: Schema.Int;
|
|
11
|
+
export declare const Uint32: Schema.Int;
|
|
12
|
+
export declare const Int8: Schema.Int;
|
|
13
|
+
export declare const Int16: Schema.Int;
|
|
14
|
+
export declare const Int32: Schema.Int;
|
|
15
|
+
export declare const Uint64: Schema.BigInt;
|
|
16
|
+
export declare const Int64: Schema.BigInt;
|
|
17
|
+
declare const ParseError_base: Schema.Class<ParseError, Schema.TaggedStruct<"BinaryParseError", {
|
|
18
|
+
readonly offset: Schema.Finite;
|
|
19
|
+
readonly expected: Schema.$Array<Schema.String>;
|
|
20
|
+
readonly found: Schema.UndefinedOr<Schema.Finite>;
|
|
21
|
+
}>, import("effect/Cause").YieldableError>;
|
|
22
|
+
export declare class ParseError extends ParseError_base {
|
|
23
|
+
get message(): string;
|
|
24
|
+
}
|
|
25
|
+
export declare const uint8: Grammar<number>;
|
|
26
|
+
export declare const uint16: Grammar<number>;
|
|
27
|
+
export declare const uint32: Grammar<number>;
|
|
28
|
+
export declare const uint64: Grammar<bigint>;
|
|
29
|
+
export declare const uint16le: Grammar<number>;
|
|
30
|
+
export declare const uint32le: Grammar<number>;
|
|
31
|
+
export declare const uint64le: Grammar<bigint>;
|
|
32
|
+
export declare const int8: Grammar<number>;
|
|
33
|
+
export declare const int16: Grammar<number>;
|
|
34
|
+
export declare const int32: Grammar<number>;
|
|
35
|
+
export declare const int64: Grammar<bigint>;
|
|
36
|
+
export declare const int16le: Grammar<number>;
|
|
37
|
+
export declare const int32le: Grammar<number>;
|
|
38
|
+
export declare const int64le: Grammar<bigint>;
|
|
39
|
+
export declare const float32: Grammar<number>;
|
|
40
|
+
export declare const float64: Grammar<number>;
|
|
41
|
+
export declare const float32le: Grammar<number>;
|
|
42
|
+
export declare const float64le: Grammar<number>;
|
|
43
|
+
/**
|
|
44
|
+
* Unsigned LEB128 within the safe integer range. Parsing accepts padded
|
|
45
|
+
* encodings of any length; printing writes the shortest one.
|
|
46
|
+
*/
|
|
47
|
+
export declare const varuint: Grammar<number>;
|
|
48
|
+
/** Zigzag-encoded LEB128, as in protobuf `sint64`, for integers from -(2 ** 52) to 2 ** 52 - 1. */
|
|
49
|
+
export declare const varint: Grammar<number>;
|
|
50
|
+
export type BitLayout = Readonly<Record<string, number>>;
|
|
51
|
+
export type Bits<Layout extends BitLayout> = {
|
|
52
|
+
readonly [K in keyof Layout]: Layout[K] extends 1 ? 0 | 1 : number;
|
|
53
|
+
};
|
|
54
|
+
export declare const bits: <const Layout extends BitLayout>(layout: Layout) => Grammar<Bits<Layout>>;
|
|
55
|
+
export declare const bytes: (count: Ref<number> | number) => Grammar<Uint8Array>;
|
|
56
|
+
export declare const lengthPrefixed: (length: Grammar<number>) => Grammar<Uint8Array>;
|
|
57
|
+
export declare const literal: (...values: ReadonlyArray<number>) => Silent;
|
|
58
|
+
export declare const ascii: (inner: Grammar<Uint8Array<ArrayBufferLike>>) => Grammar<string>;
|
|
59
|
+
export declare const utf8: (inner: Grammar<Uint8Array<ArrayBufferLike>>) => Grammar<string>;
|
|
60
|
+
export declare const parse: <A>(grammar: Grammar<A>, input: Uint8Array) => Result.Result<A, ParseError>;
|
|
61
|
+
export declare const print: <A>(grammar: Grammar<A>, value: A) => Result.Result<Uint8Array, PrintError>;
|
|
62
|
+
export declare const printChecked: typeof print;
|
|
63
|
+
export declare const codec: <S extends Schema.Top, A extends S["Encoded"]>(grammar: Grammar<A>, target: S, options?: CodecOptions) => Schema.decodeTo<S, Schema.Codec<Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, never, never>, never, never>;
|
|
64
|
+
//# sourceMappingURL=binary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"binary.d.ts","sourceRoot":"","sources":["../src/binary.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAEjE,OAAO,EAAE,KAAK,YAAY,EAAa,MAAM,YAAY,CAAA;AAEzD,OAAO,EACL,KAAK,OAAO,EAEZ,KAAK,GAAG,EACR,KAAK,MAAM,EAGZ,MAAM,WAAW,CAAA;AAElB,OAAO,EAAyB,UAAU,EAAE,MAAM,aAAa,CAAA;AAI/D,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAUjC,eAAO,MAAM,GAAG,kCAA0B,CAAA;AAC1C,eAAO,MAAM,IAAI,SAAU,MAAM,eAGhC,CAAA;AACD,eAAO,MAAM,GAAG,SAAU,MAAM,eAK/B,CAAA;AACD,eAAO,MAAM,KAAK,YAAU,CAAA;AAC5B,eAAO,MAAM,MAAM,YAAW,CAAA;AAC9B,eAAO,MAAM,MAAM,YAAW,CAAA;AAC9B,eAAO,MAAM,IAAI,YAAS,CAAA;AAC1B,eAAO,MAAM,KAAK,YAAU,CAAA;AAC5B,eAAO,MAAM,KAAK,YAAU,CAAA;AAC5B,eAAO,MAAM,MAAM,eAElB,CAAA;AACD,eAAO,MAAM,KAAK,eAEjB,CAAA;;;;;;AAED,qBAAa,UAAW,SAAQ,eAI9B;IACA,IAAa,OAAO,IAAI,MAAM,CAG7B;CACF;AAoFD,eAAO,MAAM,KAAK,iBAAmB,CAAA;AACrC,eAAO,MAAM,MAAM,iBAAoB,CAAA;AACvC,eAAO,MAAM,MAAM,iBAAoB,CAAA;AACvC,eAAO,MAAM,MAAM,iBAAqB,CAAA;AACxC,eAAO,MAAM,QAAQ,iBAA4B,CAAA;AACjD,eAAO,MAAM,QAAQ,iBAA4B,CAAA;AACjD,eAAO,MAAM,QAAQ,iBAA6B,CAAA;AAElD,eAAO,MAAM,IAAI,iBAAiB,CAAA;AAClC,eAAO,MAAM,KAAK,iBAAkB,CAAA;AACpC,eAAO,MAAM,KAAK,iBAAkB,CAAA;AACpC,eAAO,MAAM,KAAK,iBAAmB,CAAA;AACrC,eAAO,MAAM,OAAO,iBAA0B,CAAA;AAC9C,eAAO,MAAM,OAAO,iBAA0B,CAAA;AAC9C,eAAO,MAAM,OAAO,iBAA2B,CAAA;AAE/C,eAAO,MAAM,OAAO,iBAAsB,CAAA;AAC1C,eAAO,MAAM,OAAO,iBAAsB,CAAA;AAC1C,eAAO,MAAM,SAAS,iBAA8B,CAAA;AACpD,eAAO,MAAM,SAAS,iBAA8B,CAAA;AAwBpD;;;GAGG;AACH,eAAO,MAAM,OAAO,iBAcnB,CAAA;AAED,mGAAmG;AACnG,eAAO,MAAM,MAAM,iBAclB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;AAExD,MAAM,MAAM,IAAI,CAAC,MAAM,SAAS,SAAS,IAAI;IAC3C,QAAQ,EAAE,CAAC,IAAI,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM;CACnE,CAAA;AAED,eAAO,MAAM,IAAI,GAAI,KAAK,CAAC,MAAM,SAAS,SAAS,UAAU,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAuCzF,CAAA;AASD,eAAO,MAAM,KAAK,UAAW,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,KAAG,OAAO,CAAC,UAAU,CACtC,CAAA;AAEhC,eAAO,MAAM,cAAc,WAAY,OAAO,CAAC,MAAM,CAAC,KAAG,OAAO,CAAC,UAAU,CAC9B,CAAA;AAE7C,eAAO,MAAM,OAAO,cAAe,aAAa,CAAC,MAAM,CAAC,KAAG,MAS1D,CAAA;AAED,eAAO,MAAM,KAAK,kEAKhB,CAAA;AAEF,eAAO,MAAM,IAAI,kEAef,CAAA;AAEF,eAAO,MAAM,KAAK,GAAI,CAAC,WAAW,OAAO,CAAC,CAAC,CAAC,SAAS,UAAU,KAAG,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAK1F,CAAA;AAkBH,eAAO,MAAM,KAAK,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAC9E,CAAA;AAEhB,eAAO,MAAM,YAAY,EAAE,OAAO,KAAqB,CAAA;AAEvD,eAAO,MAAM,KAAK,GAAI,CAAC,SAAS,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,SAAS,CAAC,WACvD,OAAO,CAAC,CAAC,CAAC,UACX,CAAC,YACC,YAAY,2HAWvB,CAAA"}
|