@thomasfosterau/effect-parse 0.4.1 → 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 +37 -13
- package/dist/Ascii.d.ts +11 -4
- package/dist/Ascii.d.ts.map +1 -1
- package/dist/Ascii.js +7 -1
- package/dist/Ascii.js.map +1 -1
- package/dist/Binary.d.ts.map +1 -1
- package/dist/Binary.js +64 -35
- package/dist/Binary.js.map +1 -1
- package/dist/Bits.d.ts.map +1 -1
- package/dist/Bits.js +7 -4
- package/dist/Bits.js.map +1 -1
- package/dist/Compiler.d.ts +74 -2
- package/dist/Compiler.d.ts.map +1 -1
- package/dist/Compiler.js +78 -1
- package/dist/Compiler.js.map +1 -1
- package/dist/Expression.d.ts.map +1 -1
- package/dist/Expression.js +86 -74
- package/dist/Expression.js.map +1 -1
- package/dist/Input.d.ts +37 -4
- package/dist/Input.d.ts.map +1 -1
- package/dist/Input.js +42 -0
- package/dist/Input.js.map +1 -1
- package/dist/Nom.d.ts +5 -15
- package/dist/Nom.d.ts.map +1 -1
- package/dist/Nom.js.map +1 -1
- package/dist/ParseError.d.ts.map +1 -1
- package/dist/ParseError.js.map +1 -1
- package/dist/Parser.d.ts +366 -26
- package/dist/Parser.d.ts.map +1 -1
- package/dist/Parser.js +390 -19
- package/dist/Parser.js.map +1 -1
- package/dist/ParserSchema.js +1 -1
- package/dist/ParserStream.d.ts +3 -2
- package/dist/ParserStream.d.ts.map +1 -1
- package/dist/ParserStream.js +13 -9
- package/dist/ParserStream.js.map +1 -1
- package/dist/Syntax.d.ts +23 -5
- package/dist/Syntax.d.ts.map +1 -1
- package/dist/Syntax.js.map +1 -1
- package/dist/Text.d.ts.map +1 -1
- package/dist/Text.js +9 -9
- package/dist/Text.js.map +1 -1
- package/dist/Token.d.ts +55 -10
- package/dist/Token.d.ts.map +1 -1
- package/dist/Token.js +59 -21
- package/dist/Token.js.map +1 -1
- package/dist/internal/compiler/closure.d.ts.map +1 -1
- package/dist/internal/compiler/closure.js +257 -97
- package/dist/internal/compiler/closure.js.map +1 -1
- package/dist/internal/compiler/entries.d.ts +3 -1
- package/dist/internal/compiler/entries.d.ts.map +1 -1
- package/dist/internal/compiler/entries.js +3 -2
- package/dist/internal/compiler/entries.js.map +1 -1
- package/dist/internal/compiler/first.d.ts.map +1 -1
- package/dist/internal/compiler/first.js +7 -1
- package/dist/internal/compiler/first.js.map +1 -1
- package/dist/internal/compiler/graph.d.ts +17 -0
- package/dist/internal/compiler/graph.d.ts.map +1 -0
- package/dist/internal/compiler/graph.js +56 -0
- package/dist/internal/compiler/graph.js.map +1 -0
- package/dist/internal/compiler/jit.d.ts +5 -0
- package/dist/internal/compiler/jit.d.ts.map +1 -1
- package/dist/internal/compiler/jit.js +185 -96
- package/dist/internal/compiler/jit.js.map +1 -1
- package/dist/internal/compiler/registry.d.ts +10 -2
- package/dist/internal/compiler/registry.d.ts.map +1 -1
- package/dist/internal/compiler/registry.js.map +1 -1
- package/dist/internal/compiler/search.d.ts +32 -0
- package/dist/internal/compiler/search.d.ts.map +1 -0
- package/dist/internal/compiler/search.js +68 -0
- package/dist/internal/compiler/search.js.map +1 -0
- package/dist/internal/compiler/sets.d.ts.map +1 -1
- package/dist/internal/compiler/sets.js +1 -1
- package/dist/internal/compiler/sets.js.map +1 -1
- package/dist/internal/compiler/wasm.d.ts +8 -0
- package/dist/internal/compiler/wasm.d.ts.map +1 -0
- package/dist/internal/compiler/wasm.js +2769 -0
- package/dist/internal/compiler/wasm.js.map +1 -0
- package/dist/internal/compiler/wasmAsm.d.ts +199 -0
- package/dist/internal/compiler/wasmAsm.d.ts.map +1 -0
- package/dist/internal/compiler/wasmAsm.js +427 -0
- package/dist/internal/compiler/wasmAsm.js.map +1 -0
- package/dist/internal/entries.d.ts +3 -1
- package/dist/internal/entries.d.ts.map +1 -1
- package/dist/internal/entries.js.map +1 -1
- package/dist/internal/node.d.ts +58 -2
- package/dist/internal/node.d.ts.map +1 -1
- package/dist/internal/node.js +13 -3
- package/dist/internal/node.js.map +1 -1
- package/dist/internal/numbers.d.ts.map +1 -1
- package/dist/internal/numbers.js.map +1 -1
- package/dist/internal/run.d.ts +12 -0
- package/dist/internal/run.d.ts.map +1 -1
- package/dist/internal/run.js +51 -11
- package/dist/internal/run.js.map +1 -1
- package/dist/internal/stack.d.ts +6 -0
- package/dist/internal/stack.d.ts.map +1 -1
- package/dist/internal/stack.js +8 -2
- package/dist/internal/stack.js.map +1 -1
- package/dist/internal/syntax.d.ts +4 -4
- package/dist/internal/syntax.d.ts.map +1 -1
- package/dist/internal/syntax.js +18 -10
- package/dist/internal/syntax.js.map +1 -1
- package/dist/internal/text.d.ts +32 -0
- package/dist/internal/text.d.ts.map +1 -1
- package/dist/internal/text.js +68 -14
- package/dist/internal/text.js.map +1 -1
- package/dist/internal/tokens.d.ts +13 -0
- package/dist/internal/tokens.d.ts.map +1 -1
- package/dist/internal/tokens.js +59 -39
- package/dist/internal/tokens.js.map +1 -1
- package/package.json +5 -4
- package/src/Ascii.ts +42 -9
- package/src/Binary.ts +151 -71
- package/src/Bits.ts +12 -6
- package/src/Compiler.ts +101 -10
- package/src/Expression.ts +94 -81
- package/src/Input.ts +65 -6
- package/src/Nom.ts +15 -5
- package/src/ParseError.ts +3 -0
- package/src/Parser.ts +678 -127
- package/src/ParserStream.ts +37 -18
- package/src/Syntax.ts +60 -36
- package/src/Text.ts +16 -15
- package/src/Token.ts +138 -29
- package/src/internal/compiler/closure.ts +362 -135
- package/src/internal/compiler/entries.ts +4 -2
- package/src/internal/compiler/first.ts +10 -0
- package/src/internal/compiler/graph.ts +68 -0
- package/src/internal/compiler/jit.ts +284 -154
- package/src/internal/compiler/registry.ts +10 -2
- package/src/internal/compiler/search.ts +68 -0
- package/src/internal/compiler/sets.ts +7 -3
- package/src/internal/compiler/wasm.ts +3203 -0
- package/src/internal/compiler/wasmAsm.ts +484 -0
- package/src/internal/entries.ts +8 -2
- package/src/internal/node.ts +54 -4
- package/src/internal/numbers.ts +3 -1
- package/src/internal/run.ts +71 -12
- package/src/internal/stack.ts +8 -1
- package/src/internal/syntax.ts +39 -18
- package/src/internal/text.ts +97 -9
- package/src/internal/tokens.ts +96 -14
package/src/Binary.ts
CHANGED
|
@@ -44,45 +44,61 @@ export type Endianness = "big" | "little" | "native"
|
|
|
44
44
|
|
|
45
45
|
const nativeLittle = /*#__PURE__*/ new Uint8Array(/*#__PURE__*/ new Uint16Array([1]).buffer)[0] === 1
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
/** Reads a number at an offset of a state's bytes. */
|
|
48
|
+
type Read<A> = (bytes: Uint8Array, offset: number, state: State<Uint8Array>) => A
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* A `DataView` of a state's bytes, for the numbers integer arithmetic can't
|
|
52
|
+
* read: created once per state (whose input never changes), as one per
|
|
53
|
+
* read cost most of a number's time.
|
|
54
|
+
*/
|
|
55
|
+
const viewOf = (state: State<Uint8Array>): DataView => {
|
|
56
|
+
const b = state.input
|
|
57
|
+
return (state.view ??= new DataView(b.buffer, b.byteOffset, b.byteLength))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** A {@link Read} through the state's view. */
|
|
61
|
+
const viewed =
|
|
62
|
+
<A>(read: (view: DataView, offset: number) => A): Read<A> =>
|
|
63
|
+
(_, o, s) =>
|
|
64
|
+
read(viewOf(s), o)
|
|
65
|
+
|
|
66
|
+
const fixed = <A>(size: number, name: string, read: Read<A>): Parser<A, never, never, Uint8Array> => {
|
|
52
67
|
const expected = [`${size} byte${size === 1 ? "" : "s"}`]
|
|
53
|
-
return P.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
return P.described(
|
|
69
|
+
(state: State<Uint8Array>) => {
|
|
70
|
+
const pos = state.pos
|
|
71
|
+
const avail = state.end - pos
|
|
72
|
+
if (avail < size) return state.eoi("Token", expected, size - avail)
|
|
73
|
+
state.pos = pos + size
|
|
74
|
+
return read(state.input, pos, state)
|
|
75
|
+
},
|
|
76
|
+
name,
|
|
77
|
+
{ _tag: "Fixed", size, read }
|
|
78
|
+
)
|
|
62
79
|
}
|
|
63
80
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
81
|
+
// Integers of up to 32 bits are read from the bytes; the rest through a view
|
|
82
|
+
const readU24 =
|
|
83
|
+
(little: boolean): Read<number> =>
|
|
84
|
+
(b, o) =>
|
|
85
|
+
little ? b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) : (b[o] << 16) | (b[o + 1] << 8) | b[o + 2]
|
|
68
86
|
|
|
69
|
-
const readI24 = (little: boolean) => {
|
|
87
|
+
const readI24 = (little: boolean): Read<number> => {
|
|
70
88
|
const u = readU24(little)
|
|
71
|
-
return (
|
|
72
|
-
const x = u(v, o)
|
|
73
|
-
return x & 0x800000 ? x - 0x1000000 : x
|
|
74
|
-
}
|
|
89
|
+
return (b, o, s) => (u(b, o, s) << 8) >> 8
|
|
75
90
|
}
|
|
76
91
|
|
|
77
|
-
const readU128 = (little: boolean)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
92
|
+
const readU128 = (little: boolean): Read<bigint> =>
|
|
93
|
+
viewed((v, o) => {
|
|
94
|
+
const a = v.getBigUint64(o, little)
|
|
95
|
+
const b = v.getBigUint64(o + 8, little)
|
|
96
|
+
return little ? (b << 64n) | a : (a << 64n) | b
|
|
97
|
+
})
|
|
82
98
|
|
|
83
|
-
const readI128 = (little: boolean) => {
|
|
99
|
+
const readI128 = (little: boolean): Read<bigint> => {
|
|
84
100
|
const u = readU128(little)
|
|
85
|
-
return (
|
|
101
|
+
return (b, o, s) => BigInt.asIntN(128, u(b, o, s))
|
|
86
102
|
}
|
|
87
103
|
|
|
88
104
|
/**
|
|
@@ -91,7 +107,7 @@ const readI128 = (little: boolean) => {
|
|
|
91
107
|
* @since 0.1.0
|
|
92
108
|
* @category integers
|
|
93
109
|
*/
|
|
94
|
-
export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "u8", (
|
|
110
|
+
export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "u8", (b, o) => b[o])
|
|
95
111
|
|
|
96
112
|
/**
|
|
97
113
|
* A signed byte.
|
|
@@ -99,23 +115,31 @@ export const u8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
|
99
115
|
* @since 0.1.0
|
|
100
116
|
* @category integers
|
|
101
117
|
*/
|
|
102
|
-
export const i8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "i8", (
|
|
118
|
+
export const i8: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(1, "i8", (b, o) => (b[o] << 24) >> 24)
|
|
103
119
|
|
|
104
120
|
/** @since 0.1.0 @category integers */
|
|
105
|
-
export const beU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
106
|
-
|
|
121
|
+
export const beU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
122
|
+
2,
|
|
123
|
+
"beU16",
|
|
124
|
+
(b, o) => (b[o] << 8) | b[o + 1]
|
|
107
125
|
)
|
|
108
126
|
/** @since 0.1.0 @category integers */
|
|
109
|
-
export const leU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
110
|
-
|
|
127
|
+
export const leU16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
128
|
+
2,
|
|
129
|
+
"leU16",
|
|
130
|
+
(b, o) => b[o] | (b[o + 1] << 8)
|
|
111
131
|
)
|
|
112
132
|
/** @since 0.1.0 @category integers */
|
|
113
|
-
export const beI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
114
|
-
|
|
133
|
+
export const beI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
134
|
+
2,
|
|
135
|
+
"beI16",
|
|
136
|
+
(b, o) => (((b[o] << 8) | b[o + 1]) << 16) >> 16
|
|
115
137
|
)
|
|
116
138
|
/** @since 0.1.0 @category integers */
|
|
117
|
-
export const leI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
118
|
-
|
|
139
|
+
export const leI16: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
140
|
+
2,
|
|
141
|
+
"leI16",
|
|
142
|
+
(b, o) => ((b[o] | (b[o + 1] << 8)) << 16) >> 16
|
|
119
143
|
)
|
|
120
144
|
/** @since 0.1.0 @category integers */
|
|
121
145
|
export const beU24: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
@@ -142,36 +166,52 @@ export const leI24: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fix
|
|
|
142
166
|
/*#__PURE__*/ readI24(true)
|
|
143
167
|
)
|
|
144
168
|
/** @since 0.1.0 @category integers */
|
|
145
|
-
export const beU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
146
|
-
|
|
169
|
+
export const beU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
170
|
+
4,
|
|
171
|
+
"beU32",
|
|
172
|
+
(b, o) => ((b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]) >>> 0
|
|
147
173
|
)
|
|
148
174
|
/** @since 0.1.0 @category integers */
|
|
149
|
-
export const leU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
150
|
-
|
|
175
|
+
export const leU32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
176
|
+
4,
|
|
177
|
+
"leU32",
|
|
178
|
+
(b, o) => (b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)) >>> 0
|
|
151
179
|
)
|
|
152
180
|
/** @since 0.1.0 @category integers */
|
|
153
|
-
export const beI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
154
|
-
|
|
181
|
+
export const beI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
182
|
+
4,
|
|
183
|
+
"beI32",
|
|
184
|
+
(b, o) => (b[o] << 24) | (b[o + 1] << 16) | (b[o + 2] << 8) | b[o + 3]
|
|
155
185
|
)
|
|
156
186
|
/** @since 0.1.0 @category integers */
|
|
157
|
-
export const leI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
158
|
-
|
|
187
|
+
export const leI32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
188
|
+
4,
|
|
189
|
+
"leI32",
|
|
190
|
+
(b, o) => b[o] | (b[o + 1] << 8) | (b[o + 2] << 16) | (b[o + 3] << 24)
|
|
159
191
|
)
|
|
160
192
|
/** @since 0.1.0 @category integers */
|
|
161
|
-
export const beU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
162
|
-
|
|
193
|
+
export const beU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
194
|
+
8,
|
|
195
|
+
"beU64",
|
|
196
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigUint64(o, false))
|
|
163
197
|
)
|
|
164
198
|
/** @since 0.1.0 @category integers */
|
|
165
|
-
export const leU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
166
|
-
|
|
199
|
+
export const leU64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
200
|
+
8,
|
|
201
|
+
"leU64",
|
|
202
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigUint64(o, true))
|
|
167
203
|
)
|
|
168
204
|
/** @since 0.1.0 @category integers */
|
|
169
|
-
export const beI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
170
|
-
|
|
205
|
+
export const beI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
206
|
+
8,
|
|
207
|
+
"beI64",
|
|
208
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigInt64(o, false))
|
|
171
209
|
)
|
|
172
210
|
/** @since 0.1.0 @category integers */
|
|
173
|
-
export const leI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
174
|
-
|
|
211
|
+
export const leI64: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
212
|
+
8,
|
|
213
|
+
"leI64",
|
|
214
|
+
/*#__PURE__*/ viewed((v, o) => v.getBigInt64(o, true))
|
|
175
215
|
)
|
|
176
216
|
/** @since 0.1.0 @category integers */
|
|
177
217
|
export const beU128: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
@@ -199,20 +239,28 @@ export const leI128: Parser<bigint, never, never, Uint8Array> = /*#__PURE__*/ fi
|
|
|
199
239
|
)
|
|
200
240
|
|
|
201
241
|
/** @since 0.1.0 @category floats */
|
|
202
|
-
export const beF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
203
|
-
|
|
242
|
+
export const beF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
243
|
+
4,
|
|
244
|
+
"beF32",
|
|
245
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat32(o, false))
|
|
204
246
|
)
|
|
205
247
|
/** @since 0.1.0 @category floats */
|
|
206
|
-
export const leF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
207
|
-
|
|
248
|
+
export const leF32: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
249
|
+
4,
|
|
250
|
+
"leF32",
|
|
251
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat32(o, true))
|
|
208
252
|
)
|
|
209
253
|
/** @since 0.1.0 @category floats */
|
|
210
|
-
export const beF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
211
|
-
|
|
254
|
+
export const beF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
255
|
+
8,
|
|
256
|
+
"beF64",
|
|
257
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat64(o, false))
|
|
212
258
|
)
|
|
213
259
|
/** @since 0.1.0 @category floats */
|
|
214
|
-
export const leF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
215
|
-
|
|
260
|
+
export const leF64: Parser<number, never, never, Uint8Array> = /*#__PURE__*/ fixed(
|
|
261
|
+
8,
|
|
262
|
+
"leF64",
|
|
263
|
+
/*#__PURE__*/ viewed((v, o) => v.getFloat64(o, true))
|
|
216
264
|
)
|
|
217
265
|
|
|
218
266
|
const byEndian =
|
|
@@ -288,6 +336,27 @@ export const f64: (endian: Endianness) => Parser<number, never, never, Uint8Arra
|
|
|
288
336
|
|
|
289
337
|
const toCount = (n: number | bigint): number => (Predicate.isBigInt(n) ? Number(n) : n)
|
|
290
338
|
|
|
339
|
+
/** How many counts {@link byCount} keeps the parsers of. */
|
|
340
|
+
const CountCache = 256
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* `build`, keeping the parsers it built for the first {@link CountCache}
|
|
344
|
+
* counts seen: length-prefixed data repeats a few lengths, and building a
|
|
345
|
+
* parser per value cost several times the parse.
|
|
346
|
+
*/
|
|
347
|
+
const byCount = <A>(build: (n: number) => A): ((n: number | bigint) => A) => {
|
|
348
|
+
const built = new Map<number, A>()
|
|
349
|
+
return (n) => {
|
|
350
|
+
const count = toCount(n)
|
|
351
|
+
let a = built.get(count)
|
|
352
|
+
if (a === undefined) {
|
|
353
|
+
a = build(count)
|
|
354
|
+
if (built.size < CountCache) built.set(count, a)
|
|
355
|
+
}
|
|
356
|
+
return a
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
291
360
|
/**
|
|
292
361
|
* Parses a length with `count`, then takes that many tokens (winnow's
|
|
293
362
|
* `length_take`, nom's `length_data`).
|
|
@@ -298,12 +367,19 @@ const toCount = (n: number | bigint): number => (Predicate.isBigInt(n) ? Number(
|
|
|
298
367
|
export const lengthTake = <E, R, I extends Input = Uint8Array>(
|
|
299
368
|
count: Parser<number | bigint, E, R, I>
|
|
300
369
|
): Parser<Slice<I>, E, R, I> => {
|
|
301
|
-
const
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
370
|
+
const take = byCount((n) => Token.take<I>(n).step)
|
|
371
|
+
// The count compiles with the grammar: the step is built from its (see `Parser.combinator`)
|
|
372
|
+
return P.combinator(
|
|
373
|
+
[count],
|
|
374
|
+
([countStep]) =>
|
|
375
|
+
(state: State<I>) => {
|
|
376
|
+
// SAFETY: the count parser's step, interpreted or compiled, returns its output (a number or bigint) or `Fail`
|
|
377
|
+
const n = countStep(state) as number | bigint | Fail
|
|
378
|
+
if (n === Fail) return Fail
|
|
379
|
+
return take(n)(state)
|
|
380
|
+
},
|
|
381
|
+
"lengthTake"
|
|
382
|
+
)
|
|
307
383
|
}
|
|
308
384
|
|
|
309
385
|
/**
|
|
@@ -317,7 +393,10 @@ export const lengthRepeat = <E, R, I extends Input, A, E2, R2, I2 extends Input>
|
|
|
317
393
|
count: Parser<number | bigint, E, R, I>,
|
|
318
394
|
parser: Parser<A, E2, R2, I2>
|
|
319
395
|
): Parser<Array<A>, E | E2, R | R2, I & I2> =>
|
|
320
|
-
P.flatMap(
|
|
396
|
+
P.flatMap(
|
|
397
|
+
count,
|
|
398
|
+
byCount((n) => P.repeat(parser, n))
|
|
399
|
+
)
|
|
321
400
|
|
|
322
401
|
/**
|
|
323
402
|
* Parses a length with `count`, takes that many tokens, and applies
|
|
@@ -331,4 +410,5 @@ export const lengthAndThen = <E, R, I extends Input, A, E2, R2>(
|
|
|
331
410
|
count: Parser<number | bigint, E, R, I>,
|
|
332
411
|
parser: Parser<A, E2, R2, Slice<I> & Input>
|
|
333
412
|
): Parser<A, E | E2, R | R2, I> =>
|
|
413
|
+
// SAFETY: a slice of an input is itself an input (a string, a `Uint8Array` or an array), so `Slice<I>` is an `Input`
|
|
334
414
|
P.mapParser(lengthTake(count) as Parser<Slice<I> & Input, E, R, I>, P.complete(parser))
|
package/src/Bits.ts
CHANGED
|
@@ -93,7 +93,7 @@ const inUnits = <A>(state: State<any>, factor: number, step: (state: State<any>)
|
|
|
93
93
|
* @category combinators
|
|
94
94
|
*/
|
|
95
95
|
export const bits = <A, E, R>(self: Parser<A, E, R, BitInput>): Parser<A, E, R, Uint8Array> => {
|
|
96
|
-
const step = self.step
|
|
96
|
+
const step = self.step
|
|
97
97
|
return make((state) => inUnits(state, 8, step), "bits")
|
|
98
98
|
}
|
|
99
99
|
|
|
@@ -105,18 +105,24 @@ export const bits = <A, E, R>(self: Parser<A, E, R, BitInput>): Parser<A, E, R,
|
|
|
105
105
|
* @category combinators
|
|
106
106
|
*/
|
|
107
107
|
export const bytes = <A, E, R>(self: Parser<A, E, R, Uint8Array>): Parser<A, E, R, BitInput> => {
|
|
108
|
-
const step = self.step
|
|
108
|
+
const step = self.step
|
|
109
109
|
return make((state: State<any>) => inUnits(state, 1 / 8, step), "bytes")
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
const readBits = (state: State<any>, count: number): number => {
|
|
113
|
+
// SAFETY: bit-level parsers run inside `bits`, on the `Uint8Array` it was given (`BitInput` only re-indexes it in bits)
|
|
113
114
|
const input = state.input as Uint8Array
|
|
114
115
|
let pos = state.pos
|
|
115
116
|
let value = 0
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
pos
|
|
117
|
+
let left = count
|
|
118
|
+
// As many bits as the current byte holds at once, not one at a time
|
|
119
|
+
while (left > 0) {
|
|
120
|
+
const avail = 8 - (pos & 7)
|
|
121
|
+
const n = left < avail ? left : avail
|
|
122
|
+
// Multiplying, not shifting: the value can have up to 53 bits
|
|
123
|
+
value = value * (1 << n) + ((input[pos >>> 3] >>> (avail - n)) & ((1 << n) - 1))
|
|
124
|
+
pos += n
|
|
125
|
+
left -= n
|
|
120
126
|
}
|
|
121
127
|
state.pos = pos
|
|
122
128
|
return value
|
package/src/Compiler.ts
CHANGED
|
@@ -4,11 +4,15 @@
|
|
|
4
4
|
* `Parser.parseSync`, `Parser.parsePeek`, `Parser.parsePartial` and
|
|
5
5
|
* `Parser.parseEffect` faster, from any `offset`.
|
|
6
6
|
*
|
|
7
|
-
* There are
|
|
8
|
-
*
|
|
7
|
+
* There are three compilers, one function each, so a program bundles only
|
|
8
|
+
* the ones it uses, and a program that compiles nothing bundles none:
|
|
9
9
|
*
|
|
10
|
+
* - {@link wasm} scans the input in WebAssembly and builds the value with
|
|
11
|
+
* generated JavaScript. It is the fastest on strings and bytes, and uses
|
|
12
|
+
* the JIT for array input and streaming, and where WebAssembly or
|
|
13
|
+
* `new Function` is unavailable.
|
|
10
14
|
* - {@link jit} generates code with `new Function` where it is allowed, and
|
|
11
|
-
* otherwise uses the closure compiler.
|
|
15
|
+
* otherwise uses the closure compiler.
|
|
12
16
|
* - {@link closure} builds specialized closures only. It never generates
|
|
13
17
|
* code, and a bundle that uses only it contains no `new Function`, so it
|
|
14
18
|
* is the choice under a Content Security Policy, where even probing for
|
|
@@ -32,7 +36,9 @@
|
|
|
32
36
|
* **Results and errors are unchanged.** Compiled code records no
|
|
33
37
|
* diagnostics. When it fails, the parser runs again, interpreted, from the
|
|
34
38
|
* same offset, so every `ParseError` (offset, expected, context, cause and
|
|
35
|
-
* message) is exactly the one the interpreter reports.
|
|
39
|
+
* message) is exactly the one the interpreter reports. Running out of
|
|
40
|
+
* partial input is not a failure that needs it: compiled code reports
|
|
41
|
+
* `Incomplete` where the interpreter does, with the same `needed`.
|
|
36
42
|
*
|
|
37
43
|
* **Callbacks should be pure.** On an input that fails, the callbacks given
|
|
38
44
|
* to `map`, `filter`, `tryMap`, `tap`, `fold`, ... may run twice, once
|
|
@@ -41,19 +47,57 @@
|
|
|
41
47
|
* nor those of a branch that is certain to fail, and token-set predicates
|
|
42
48
|
* may be called once per character code and cached.
|
|
43
49
|
*
|
|
44
|
-
* `Parser.parsePartial
|
|
45
|
-
* handles partial input
|
|
46
|
-
*
|
|
50
|
+
* `Parser.parsePartial`, `ParserStream.parseAll` and
|
|
51
|
+
* `ParserStream.parseMany` run a build that handles partial input;
|
|
52
|
+
* `parseMany` runs it once per value and parses a value again, interpreted,
|
|
53
|
+
* only when it fails. `Parser.parseRecoverable`, `Syntax`
|
|
54
|
+
* parses and runs with the `trace` option are always interpreted. Parsers that
|
|
47
55
|
* can't be described, such as `Parser.make` and `Parser.gen`, run
|
|
48
|
-
* interpreted inside compiled code
|
|
56
|
+
* interpreted inside compiled code; the parsers given to a
|
|
57
|
+
* `Parser.combinator` compile, and its step is built from theirs.
|
|
49
58
|
*
|
|
50
59
|
* @since 0.2.0
|
|
51
60
|
*/
|
|
52
|
-
import type { Input } from "./Input.js"
|
|
53
|
-
import type { Parser } from "./Parser.js"
|
|
61
|
+
import type { Fail, Input, State } from "./Input.js"
|
|
62
|
+
import type { Parser, RunOptions } from "./Parser.js"
|
|
54
63
|
import { backend as closureBackend } from "./internal/compiler/closure.js"
|
|
55
64
|
import { backend as jitBackend } from "./internal/compiler/jit.js"
|
|
56
65
|
import { register } from "./internal/compiler/registry.js"
|
|
66
|
+
import { backend as wasmBackend } from "./internal/compiler/wasm.js"
|
|
67
|
+
import { prepare } from "./internal/run.js"
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Compiles a parser with the WebAssembly compiler. Returns the same parser.
|
|
71
|
+
*
|
|
72
|
+
* The input is copied into WebAssembly memory and scanned there, by code
|
|
73
|
+
* generated from the grammar; a JavaScript function generated with
|
|
74
|
+
* `new Function` then builds the value from what the scan recorded, with
|
|
75
|
+
* the same callbacks and the same objects as the JIT. Parts of the grammar
|
|
76
|
+
* with no WebAssembly form (such as `Parser.make`, `flatMap`, or `filter`
|
|
77
|
+
* on a value) run their JIT-compiled code from inside the scan.
|
|
78
|
+
*
|
|
79
|
+
* It uses the JIT (see {@link jit}) for array input, for the streaming build
|
|
80
|
+
* of `Parser.parsePartial` and `ParserStream`, and where `WebAssembly` or
|
|
81
|
+
* `new Function` is unavailable: a Content Security Policy needs both
|
|
82
|
+
* `'wasm-unsafe-eval'` and `'unsafe-eval'` for it. Short strings with the
|
|
83
|
+
* same characters may be returned as one string, which no program can tell
|
|
84
|
+
* apart from two.
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```ts
|
|
88
|
+
* import { Ascii, Compiler, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
89
|
+
*
|
|
90
|
+
* const list = Compiler.wasm(Parser.separated(Ascii.decInt, Token.literal(",")))
|
|
91
|
+
* Parser.parse(list, "1,2,3") // Result.succeed([1, 2, 3])
|
|
92
|
+
* ```
|
|
93
|
+
*
|
|
94
|
+
* @since 0.5.0
|
|
95
|
+
* @category compilation
|
|
96
|
+
*/
|
|
97
|
+
export const wasm = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Parser<A, E, R, I> => {
|
|
98
|
+
register(self, wasmBackend)
|
|
99
|
+
return self
|
|
100
|
+
}
|
|
57
101
|
|
|
58
102
|
/**
|
|
59
103
|
* Compiles a parser with the JIT: generated code where `new Function` is
|
|
@@ -96,3 +140,50 @@ export const closure = <A, E, R, I extends Input>(self: Parser<A, E, R, I>): Par
|
|
|
96
140
|
register(self, closureBackend)
|
|
97
141
|
return self
|
|
98
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Prepares a parser for running many times on states you own, as
|
|
146
|
+
* `Parser.run` does: for drivers that make one small run per token, such as
|
|
147
|
+
* a lexer over a buffer. `Parser.run` looks the parser's compiled form up
|
|
148
|
+
* on every run, which costs about a quarter of a small token's time; the
|
|
149
|
+
* function returned here looks it up once (again only when the input kind
|
|
150
|
+
* changes, or between complete and partial states) and calls the compiled
|
|
151
|
+
* code directly.
|
|
152
|
+
*
|
|
153
|
+
* It returns exactly what `Parser.run(self, state, options)` returns, and
|
|
154
|
+
* leaves the state as it would: the output with the cursor where the
|
|
155
|
+
* parser stopped, or `Fail` with the interpreter's failure (or, with
|
|
156
|
+
* `diagnostics: false`, only the mode). Compile the parser first with
|
|
157
|
+
* {@link jit} or {@link closure}; a parser that isn't compiled runs
|
|
158
|
+
* interpreted. The compiled form is looked up on the first run, so compile
|
|
159
|
+
* the parser before using the runner: compiling it again later doesn't
|
|
160
|
+
* reach a runner that has already run.
|
|
161
|
+
*
|
|
162
|
+
* @example
|
|
163
|
+
* ```ts
|
|
164
|
+
* import { Ascii, Compiler, Input, Parser, Token } from "@thomasfosterau/effect-parse"
|
|
165
|
+
*
|
|
166
|
+
* const token = Compiler.jit(Parser.preceded(Ascii.space0, Parser.alt(Ascii.alpha1, Token.oneOf("+-"))))
|
|
167
|
+
* const next = Compiler.runner(token)
|
|
168
|
+
*
|
|
169
|
+
* const tokens = (text: string): Array<string> => {
|
|
170
|
+
* const state = new Input.State(text)
|
|
171
|
+
* const out: Array<string> = []
|
|
172
|
+
* while (state.pos < state.end) {
|
|
173
|
+
* const t = next(state.reset(state.pos))
|
|
174
|
+
* if (t === Input.Fail) break
|
|
175
|
+
* out.push(t)
|
|
176
|
+
* }
|
|
177
|
+
* return out
|
|
178
|
+
* }
|
|
179
|
+
*
|
|
180
|
+
* tokens("a + b") // ["a", "+", "b"]
|
|
181
|
+
* ```
|
|
182
|
+
*
|
|
183
|
+
* @since 0.5.0
|
|
184
|
+
* @category running
|
|
185
|
+
*/
|
|
186
|
+
export const runner = <A, E, R, I extends Input>(
|
|
187
|
+
self: Parser<A, E, R, I>,
|
|
188
|
+
options?: RunOptions
|
|
189
|
+
): ((state: State<I>) => A | Fail) => prepare(self, self.step, options?.diagnostics !== false)
|