@futpib/parser 1.0.0 → 1.0.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/build/apk.d.ts +29 -3
- package/build/apkParser.d.ts +15 -2
- package/build/apkParser.js +70 -41
- package/build/apkParser.test.js +2 -2
- package/build/apkUnparser.d.ts +4 -0
- package/build/apkUnparser.js +90 -0
- package/build/apkUnparser.test.d.ts +1 -0
- package/build/apkUnparser.test.js +26 -0
- package/build/arbitraryFileSystemEntry.js +1 -1
- package/build/arbitraryZip.d.ts +1 -1
- package/build/arbitraryZip.js +13 -19
- package/build/arbitraryZipPermissions.d.ts +1 -8
- package/build/arbitraryZipPermissions.js +1 -16
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arbitraryZipStream.js +3 -3
- package/build/arrayParser.d.ts +1 -1
- package/build/arrayParser.js +2 -2
- package/build/arrayParser.test.js +2 -2
- package/build/arrayUnparser.d.ts +2 -0
- package/build/arrayUnparser.js +8 -0
- package/build/bsonParser.test.js +2 -2
- package/build/customInvariant.d.ts +4 -0
- package/build/customInvariant.js +11 -0
- package/build/debugLogParser.d.ts +1 -1
- package/build/debugLogParser.js +1 -1
- package/build/elementParser.d.ts +2 -2
- package/build/elementParser.js +1 -1
- package/build/endOfInputParser.d.ts +2 -2
- package/build/exactElementParser.d.ts +1 -1
- package/build/exactSequenceParser.js +1 -1
- package/build/index.d.ts +5 -2
- package/build/index.js +3 -0
- package/build/inputReader.d.ts +3 -3
- package/build/inputReader.js +6 -6
- package/build/inputReader.test.js +7 -7
- package/build/javaKeyStore.d.ts +1 -0
- package/build/javaKeyStore.js +1 -0
- package/build/javaKeyStoreParser.d.ts +2 -0
- package/build/javaKeyStoreParser.js +67 -0
- package/build/javaKeyStoreParser.test.d.ts +1 -0
- package/build/javaKeyStoreParser.test.js +16 -0
- package/build/javaKeystoreParser.d.ts +2 -0
- package/build/javaKeystoreParser.js +7 -0
- package/build/jsonParser.js +3 -2
- package/build/jsonParser.test.js +2 -2
- package/build/listParser.d.ts +1 -1
- package/build/listParser.js +5 -5
- package/build/negativeLookahead.d.ts +1 -1
- package/build/negativeLookahead.js +16 -18
- package/build/negativeLookaheadParser.d.ts +2 -0
- package/build/negativeLookaheadParser.js +18 -0
- package/build/optionalParser.d.ts +1 -1
- package/build/optionalParser.js +2 -2
- package/build/parser.d.ts +3 -3
- package/build/parser.js +3 -3
- package/build/parser.test.js +16 -16
- package/build/parserAccessorParser.d.ts +1 -1
- package/build/parserConsumedSequenceParser.d.ts +2 -0
- package/build/parserConsumedSequenceParser.js +17 -0
- package/build/parserContext.d.ts +6 -6
- package/build/parserContext.js +15 -14
- package/build/parserContext.test.js +7 -7
- package/build/parserCreatorCompose.d.ts +3 -3
- package/build/parserCreatorCompose.js +2 -2
- package/build/parserImplementationInvariant.d.ts +1 -1
- package/build/parserImplementationInvariant.js +2 -2
- package/build/parserInputCompanion.d.ts +20 -0
- package/build/parserInputCompanion.js +30 -0
- package/build/parserInvariant.d.ts +1 -1
- package/build/parserInvariant.js +1 -1
- package/build/quantifierParser.d.ts +2 -0
- package/build/quantifierParser.js +17 -0
- package/build/sequenceBuffer.d.ts +3 -3
- package/build/sequenceBuffer.js +6 -6
- package/build/sequenceBuffer.test.js +2 -2
- package/build/sequenceUnparser.d.ts +2 -0
- package/build/sequenceUnparser.js +6 -0
- package/build/sliceBoundedParser.d.ts +1 -1
- package/build/sliceBoundedParser.js +1 -1
- package/build/sliceBoundedParser.test.js +2 -2
- package/build/terminatedArrayParser.d.ts +1 -1
- package/build/terminatedArrayParser.js +3 -3
- package/build/uint8Array.d.ts +1 -0
- package/build/uint8Array.js +7 -0
- package/build/unparser.d.ts +8 -0
- package/build/unparser.js +104 -0
- package/build/unparser.test.d.ts +1 -0
- package/build/unparser.test.js +150 -0
- package/build/unparserContext.d.ts +31 -0
- package/build/unparserContext.js +74 -0
- package/build/unparserError.d.ts +9 -0
- package/build/unparserError.js +9 -0
- package/build/unparserImplementationInvariant.d.ts +2 -0
- package/build/unparserImplementationInvariant.js +5 -0
- package/build/unparserInputCompanion.d.ts +15 -0
- package/build/unparserInputCompanion.js +13 -0
- package/build/unparserOutputCompanion.d.ts +15 -0
- package/build/unparserOutputCompanion.js +13 -0
- package/build/zip.d.ts +9 -17
- package/build/zipParser.d.ts +13 -10
- package/build/zipParser.js +48 -60
- package/build/zipParser.test.js +2 -7
- package/build/zipUnparser.d.ts +5 -0
- package/build/zipUnparser.js +171 -0
- package/build/zipUnparser.test.d.ts +1 -0
- package/build/zipUnparser.test.js +80 -0
- package/package.json +4 -2
- package/src/apk.ts +35 -3
- package/src/apkParser.test.ts +2 -2
- package/src/apkParser.test.ts.md +114 -111
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +150 -85
- package/src/apkUnparser.test.ts +37 -0
- package/src/apkUnparser.ts +120 -0
- package/src/arbitraryFileSystemEntry.ts +2 -4
- package/src/arbitraryZip.ts +20 -27
- package/src/arbitraryZipPermissions.ts +0 -25
- package/src/arbitraryZipStream.ts +4 -4
- package/src/arrayParser.test.ts +3 -3
- package/src/arrayParser.ts +3 -2
- package/src/arrayUnparser.ts +13 -0
- package/src/bsonParser.test.ts +2 -2
- package/src/bsonParser.ts +3 -3
- package/src/{parserInvariant.ts → customInvariant.ts} +1 -1
- package/src/debugLogParser.ts +1 -1
- package/src/elementParser.ts +3 -3
- package/src/endOfInputParser.ts +4 -4
- package/src/exactElementParser.ts +1 -1
- package/src/exactSequenceParser.ts +2 -2
- package/src/index.ts +15 -2
- package/src/inputReader.test.ts +7 -7
- package/src/inputReader.ts +5 -5
- package/src/javaKeyStore.ts +0 -0
- package/src/javaKeyStoreParser.test.ts +23 -0
- package/src/javaKeyStoreParser.test.ts.md +103 -0
- package/src/javaKeyStoreParser.test.ts.snap +0 -0
- package/src/javaKeyStoreParser.ts +136 -0
- package/src/jsonParser.test.ts +2 -2
- package/src/jsonParser.ts +13 -12
- package/src/listParser.ts +6 -6
- package/src/negativeLookaheadParser.ts +24 -0
- package/src/optionalParser.ts +3 -3
- package/src/parser.test.ts +19 -17
- package/src/parser.ts +7 -7
- package/src/parserAccessorParser.ts +1 -1
- package/src/parserConsumedSequenceParser.ts +20 -0
- package/src/parserContext.test.ts +7 -7
- package/src/parserContext.ts +18 -14
- package/src/parserCreatorCompose.ts +6 -6
- package/src/parserImplementationInvariant.ts +2 -2
- package/src/{inputCompanion.ts → parserInputCompanion.ts} +10 -6
- package/src/quantifierParser.ts +25 -0
- package/src/sequenceBuffer.test.ts +2 -2
- package/src/sequenceBuffer.ts +5 -5
- package/src/sequenceUnparser.ts +9 -0
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +2 -2
- package/src/terminatedArrayParser.ts +3 -3
- package/src/uint8Array.ts +10 -0
- package/src/unparser.test.ts +221 -0
- package/src/unparser.ts +209 -0
- package/src/unparserContext.ts +127 -0
- package/src/unparserError.ts +12 -0
- package/src/unparserImplementationInvariant.ts +6 -0
- package/src/unparserOutputCompanion.ts +24 -0
- package/src/zip.ts +10 -22
- package/src/zipParser.test.ts +2 -8
- package/src/zipParser.ts +147 -129
- package/src/zipUnparser.test.ts +119 -0
- package/src/zipUnparser.ts +239 -0
- package/src/negativeLookahead.ts +0 -26
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import * as fc from 'fast-check';
|
|
3
|
+
import { runUnparser, Unparser } from './unparser.js';
|
|
4
|
+
import { stringUnparserOutputCompanion } from './unparserOutputCompanion.js';
|
|
5
|
+
import { testProp } from '@fast-check/ava';
|
|
6
|
+
|
|
7
|
+
test('writeLater', async t => {
|
|
8
|
+
const stringToBeWrittenBefore = 'before writeLater\n';
|
|
9
|
+
const stringToBeWrittenLater = 'written later\n';
|
|
10
|
+
const stringToBeWrittenAfter = 'after writeLater\n';
|
|
11
|
+
|
|
12
|
+
let expectedPositionA: number | undefined;
|
|
13
|
+
let expectedPositionB: number | undefined;
|
|
14
|
+
|
|
15
|
+
let actualPositionAFromWithinWriteLater: number | undefined;
|
|
16
|
+
let actualPositionBFromWithinWriteLater: number | undefined;
|
|
17
|
+
|
|
18
|
+
const unparser: Unparser<void, string> = async function * (_input, unparserContext) {
|
|
19
|
+
yield stringToBeWrittenBefore
|
|
20
|
+
|
|
21
|
+
expectedPositionA = unparserContext.position;
|
|
22
|
+
|
|
23
|
+
const writeLater = yield * unparserContext.writeLater(stringToBeWrittenLater.length);
|
|
24
|
+
|
|
25
|
+
expectedPositionB = unparserContext.position;
|
|
26
|
+
|
|
27
|
+
yield stringToBeWrittenAfter;
|
|
28
|
+
|
|
29
|
+
yield * unparserContext.writeEarlier(writeLater, async function * (_input, unparserContext) {
|
|
30
|
+
actualPositionAFromWithinWriteLater = unparserContext.position;
|
|
31
|
+
|
|
32
|
+
yield stringToBeWrittenLater;
|
|
33
|
+
|
|
34
|
+
actualPositionBFromWithinWriteLater = unparserContext.position;
|
|
35
|
+
}, undefined);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const stringsAsyncIterable = runUnparser(unparser, undefined, stringUnparserOutputCompanion);
|
|
39
|
+
|
|
40
|
+
const strings: string[] = [];
|
|
41
|
+
|
|
42
|
+
for await (const string of stringsAsyncIterable) {
|
|
43
|
+
strings.push(string);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const string = strings.join('');
|
|
47
|
+
|
|
48
|
+
t.is(string, stringToBeWrittenBefore + stringToBeWrittenLater + stringToBeWrittenAfter);
|
|
49
|
+
|
|
50
|
+
t.is(actualPositionAFromWithinWriteLater, expectedPositionA, 'position a');
|
|
51
|
+
t.is(actualPositionBFromWithinWriteLater, expectedPositionB, 'position b');
|
|
52
|
+
|
|
53
|
+
t.is(expectedPositionA, stringToBeWrittenBefore.length);
|
|
54
|
+
t.is(expectedPositionB, stringToBeWrittenBefore.length + stringToBeWrittenLater.length);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('writeLater one deep', async t => {
|
|
58
|
+
const unparser: Unparser<void, string> = async function * (_input, unparserContext) {
|
|
59
|
+
yield '(';
|
|
60
|
+
const writeLaterA = yield * unparserContext.writeLater(3);
|
|
61
|
+
yield ')';
|
|
62
|
+
|
|
63
|
+
yield * unparserContext.writeEarlier(writeLaterA, async function * (_input, unparserContext) {
|
|
64
|
+
yield '[';
|
|
65
|
+
const writeLaterB = yield * unparserContext.writeLater(1);
|
|
66
|
+
yield ']';
|
|
67
|
+
|
|
68
|
+
yield * unparserContext.writeEarlier(writeLaterB, async function * (_input, unparserContext) {
|
|
69
|
+
yield 'a';
|
|
70
|
+
}, undefined);
|
|
71
|
+
}, undefined);
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const stringsAsyncIterable = runUnparser(unparser, undefined, stringUnparserOutputCompanion);
|
|
75
|
+
|
|
76
|
+
const strings: string[] = [];
|
|
77
|
+
|
|
78
|
+
for await (const string of stringsAsyncIterable) {
|
|
79
|
+
strings.push(string);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const string = strings.join('');
|
|
83
|
+
|
|
84
|
+
t.is(string, '([a])');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test('writeLater deep', async t => {
|
|
88
|
+
const unparser: Unparser<void, string> = async function * (_input, unparserContext) {
|
|
89
|
+
yield '(';
|
|
90
|
+
const writeLaterA = yield * unparserContext.writeLater(5);
|
|
91
|
+
yield ')';
|
|
92
|
+
|
|
93
|
+
let writeLaterC: typeof writeLaterA;
|
|
94
|
+
|
|
95
|
+
yield * unparserContext.writeEarlier(writeLaterA, async function * (_input, unparserContext) {
|
|
96
|
+
yield '[';
|
|
97
|
+
const writeLaterB = yield * unparserContext.writeLater(3);
|
|
98
|
+
yield ']';
|
|
99
|
+
|
|
100
|
+
yield * unparserContext.writeEarlier(writeLaterB, async function * (_input, unparserContext) {
|
|
101
|
+
yield '{';
|
|
102
|
+
writeLaterC = yield * unparserContext.writeLater(1);
|
|
103
|
+
yield '}';
|
|
104
|
+
}, undefined);
|
|
105
|
+
}, undefined);
|
|
106
|
+
|
|
107
|
+
yield * unparserContext.writeEarlier(writeLaterC!, async function * (_input, unparserContext) {
|
|
108
|
+
yield 'a';
|
|
109
|
+
}, undefined);
|
|
110
|
+
|
|
111
|
+
yield 'z';
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const stringsAsyncIterable = runUnparser(unparser, undefined, stringUnparserOutputCompanion);
|
|
115
|
+
|
|
116
|
+
const strings: string[] = [];
|
|
117
|
+
|
|
118
|
+
for await (const string of stringsAsyncIterable) {
|
|
119
|
+
strings.push(string);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const string = strings.join('');
|
|
123
|
+
|
|
124
|
+
t.is(string, '([{a}])z');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
testProp(
|
|
128
|
+
'writeLater positions and lengths',
|
|
129
|
+
[
|
|
130
|
+
fc.array(
|
|
131
|
+
fc.oneof(
|
|
132
|
+
fc.array(
|
|
133
|
+
fc.oneof(
|
|
134
|
+
fc.array(
|
|
135
|
+
fc.string(),
|
|
136
|
+
),
|
|
137
|
+
fc.string(),
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
fc.string(),
|
|
141
|
+
),
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
async (t, stringArray3) => {
|
|
145
|
+
const numberUnparser: Unparser<number, string> = async function * (input, unparserContext) {
|
|
146
|
+
yield input.toString().padStart(8, '0');
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const createLengthPrefixedUnparser = <T>(
|
|
150
|
+
unparser: Unparser<T, string>,
|
|
151
|
+
): Unparser<T, string> => {
|
|
152
|
+
return async function * (input, unparserContext) {
|
|
153
|
+
const length = yield * unparserContext.writeLater(8);
|
|
154
|
+
yield * unparser(input, unparserContext);
|
|
155
|
+
yield * unparserContext.writeEarlier(length, numberUnparser, unparserContext.position - length.positionEnd);
|
|
156
|
+
};
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const unparser0: Unparser<string, string> = createLengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
160
|
+
yield input;
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const unparser1: Unparser<string[], string> = createLengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
164
|
+
for (const string of input) {
|
|
165
|
+
yield * unparser0(string, unparserContext);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const unparser2: Unparser<(string | string[])[], string> = createLengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
170
|
+
for (const stringArray2 of input) {
|
|
171
|
+
if (typeof stringArray2 === 'string') {
|
|
172
|
+
yield * unparser0(stringArray2, unparserContext);
|
|
173
|
+
} else {
|
|
174
|
+
yield * unparser1(stringArray2, unparserContext);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
const unparser3: Unparser<(string | (string | string[])[])[], string> = createLengthPrefixedUnparser(async function * (input, unparserContext) {
|
|
180
|
+
for (const stringArray3 of input) {
|
|
181
|
+
if (typeof stringArray3 === 'string') {
|
|
182
|
+
yield * unparser0(stringArray3, unparserContext);
|
|
183
|
+
} else {
|
|
184
|
+
yield * unparser2(stringArray3, unparserContext);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const stringsAsyncIterable = runUnparser(unparser3, stringArray3, stringUnparserOutputCompanion);
|
|
190
|
+
|
|
191
|
+
const strings: string[] = [];
|
|
192
|
+
|
|
193
|
+
for await (const string of stringsAsyncIterable) {
|
|
194
|
+
strings.push(string);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const actualString = strings.join('');
|
|
198
|
+
|
|
199
|
+
type DeepArray<T> = T | DeepArray<T>[];
|
|
200
|
+
|
|
201
|
+
function getExpectedString(stringArray: DeepArray<string>): string {
|
|
202
|
+
let expectedString = stringArray;
|
|
203
|
+
|
|
204
|
+
if (typeof stringArray !== 'string') {
|
|
205
|
+
expectedString = stringArray.map(getExpectedString).join('');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return [
|
|
209
|
+
expectedString.length.toString().padStart(8, '0'),
|
|
210
|
+
expectedString,
|
|
211
|
+
].join('');
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const expectedString = getExpectedString(stringArray3);
|
|
215
|
+
|
|
216
|
+
t.deepEqual(actualString, expectedString);
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
verbose: true,
|
|
220
|
+
},
|
|
221
|
+
);
|
package/src/unparser.ts
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
import { DeriveSequenceElement } from "./sequence.js";
|
|
3
|
+
import { UnparserContext, UnparserContextImplementation, WriteEarlier, WriteLater } from "./unparserContext.js";
|
|
4
|
+
import { UnparserOutputCompanion } from "./unparserOutputCompanion.js";
|
|
5
|
+
import { unparserImplementationInvariant } from "./unparserImplementationInvariant.js";
|
|
6
|
+
|
|
7
|
+
type UnparserIterableValue<Sequence, Element> =
|
|
8
|
+
| Sequence
|
|
9
|
+
| Element
|
|
10
|
+
| WriteLater<Sequence, Element>
|
|
11
|
+
| WriteEarlier<Sequence, Element>
|
|
12
|
+
;
|
|
13
|
+
|
|
14
|
+
type UnparserIterableValue_<Sequence, Element> =
|
|
15
|
+
| Sequence
|
|
16
|
+
| WriteLater<Sequence, Element>
|
|
17
|
+
| WriteEarlier<Sequence, Element>
|
|
18
|
+
;
|
|
19
|
+
|
|
20
|
+
export type UnparserResult<Sequence, Element> = AsyncIterable<UnparserIterableValue<Sequence, Element>>;
|
|
21
|
+
|
|
22
|
+
type UnparserResult_<Sequence, Element> = AsyncIterable<UnparserIterableValue_<Sequence, Element>>;
|
|
23
|
+
|
|
24
|
+
export type Unparser<
|
|
25
|
+
Input,
|
|
26
|
+
Sequence,
|
|
27
|
+
Element = DeriveSequenceElement<Sequence>,
|
|
28
|
+
> = (
|
|
29
|
+
input: Input,
|
|
30
|
+
unparserContext: UnparserContext<Sequence, Element>,
|
|
31
|
+
) => UnparserResult<Sequence, Element>;
|
|
32
|
+
|
|
33
|
+
async function * onYield<T>(asyncIterable: AsyncIterable<T>, onYield: (value: T) => void): AsyncIterable<T> {
|
|
34
|
+
for await (const value of asyncIterable) {
|
|
35
|
+
onYield(value);
|
|
36
|
+
yield value;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function * elementsToSequences<
|
|
41
|
+
Sequence,
|
|
42
|
+
Element,
|
|
43
|
+
>(
|
|
44
|
+
values: UnparserResult<Sequence, Element>,
|
|
45
|
+
unparserOutputCompanion: UnparserOutputCompanion<Sequence, Element>,
|
|
46
|
+
): UnparserResult_<Sequence, Element> {
|
|
47
|
+
let elements: Element[] = [];
|
|
48
|
+
|
|
49
|
+
for await (const value of values) {
|
|
50
|
+
if (
|
|
51
|
+
value instanceof WriteLater
|
|
52
|
+
|| value instanceof WriteEarlier
|
|
53
|
+
) {
|
|
54
|
+
if (elements.length > 0) {
|
|
55
|
+
const sequence = unparserOutputCompanion.from(elements);
|
|
56
|
+
yield sequence;
|
|
57
|
+
elements = [];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
yield value;
|
|
61
|
+
} else if (unparserOutputCompanion.is(value)) {
|
|
62
|
+
if (unparserOutputCompanion.length(value) === 0) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (elements.length > 0) {
|
|
67
|
+
const sequence = unparserOutputCompanion.from(elements);
|
|
68
|
+
yield sequence;
|
|
69
|
+
elements = [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
yield value;
|
|
73
|
+
} else {
|
|
74
|
+
elements.push(value);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (elements.length > 0) {
|
|
79
|
+
const sequence = unparserOutputCompanion.from(elements);
|
|
80
|
+
yield sequence;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function wrapUnparserResult<Sequence, Element>(
|
|
85
|
+
unparserResult: UnparserResult<Sequence, Element>,
|
|
86
|
+
unparserContext: UnparserContextImplementation<Sequence, Element>,
|
|
87
|
+
unparserOutputCompanion: UnparserOutputCompanion<Sequence, Element>,
|
|
88
|
+
): UnparserResult_<Sequence, Element> {
|
|
89
|
+
return onYield(
|
|
90
|
+
elementsToSequences(
|
|
91
|
+
unparserResult,
|
|
92
|
+
unparserOutputCompanion,
|
|
93
|
+
),
|
|
94
|
+
value => unparserContext.handleYield(value),
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function * runUnparser<
|
|
99
|
+
Input,
|
|
100
|
+
Sequence,
|
|
101
|
+
Element = DeriveSequenceElement<Sequence>,
|
|
102
|
+
>(
|
|
103
|
+
unparser: Unparser<Input, Sequence, Element>,
|
|
104
|
+
input: Input,
|
|
105
|
+
unparserOutputCompanion: UnparserOutputCompanion<Sequence, Element>,
|
|
106
|
+
): AsyncIterable<Sequence> {
|
|
107
|
+
const unparserContext = new UnparserContextImplementation(unparserOutputCompanion);
|
|
108
|
+
|
|
109
|
+
const values = unparser(input, unparserContext);
|
|
110
|
+
const valuesWithoutElements = wrapUnparserResult(
|
|
111
|
+
values,
|
|
112
|
+
unparserContext,
|
|
113
|
+
unparserOutputCompanion,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
const outputQueue: Array<WriteLater<Sequence, Element> | Sequence> = [];
|
|
117
|
+
|
|
118
|
+
type Iterator = AsyncIterator<UnparserIterableValue_<Sequence, Element>, void>;
|
|
119
|
+
type PushOutput = (value: Sequence | WriteLater<Sequence, Element>) => void;
|
|
120
|
+
type FinishOutput = () => void;
|
|
121
|
+
|
|
122
|
+
const iteratorStack: [ Iterator, PushOutput, FinishOutput ][] = [
|
|
123
|
+
[
|
|
124
|
+
valuesWithoutElements[Symbol.asyncIterator](),
|
|
125
|
+
value => outputQueue.push(value),
|
|
126
|
+
() => {},
|
|
127
|
+
],
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
while (true) {
|
|
131
|
+
while (
|
|
132
|
+
outputQueue.length > 0
|
|
133
|
+
&& !(outputQueue[0] instanceof WriteLater)
|
|
134
|
+
) {
|
|
135
|
+
yield outputQueue.shift() as Sequence;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (iteratorStack.length === 0) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const [ iterator, pushOutput, finishOutput ] = iteratorStack[0];
|
|
143
|
+
|
|
144
|
+
const iteratorResult = await iterator.next();
|
|
145
|
+
|
|
146
|
+
if (iteratorResult.done) {
|
|
147
|
+
finishOutput();
|
|
148
|
+
iteratorStack.shift();
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const { value } = iteratorResult;
|
|
153
|
+
|
|
154
|
+
if (value instanceof WriteEarlier) {
|
|
155
|
+
const { writeLater, unparserResult, unparserContext } = value;
|
|
156
|
+
|
|
157
|
+
invariant(
|
|
158
|
+
unparserContext instanceof UnparserContextImplementation,
|
|
159
|
+
'UnparserContext not an instance of UnparserContextImplementation.',
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
function getOutputQueueWriteLaterIndex() {
|
|
163
|
+
const outputQueueWriteLaterIndex = outputQueue.indexOf(writeLater);
|
|
164
|
+
|
|
165
|
+
unparserImplementationInvariant(
|
|
166
|
+
outputQueueWriteLaterIndex !== -1,
|
|
167
|
+
[
|
|
168
|
+
'WriteLater has already been written or was not yielded yet.',
|
|
169
|
+
'WriteLater stack: %s',
|
|
170
|
+
'End of WriteLater stack.',
|
|
171
|
+
],
|
|
172
|
+
writeLater.stack,
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
return outputQueueWriteLaterIndex;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const unparserResultWithoutElements = wrapUnparserResult(
|
|
179
|
+
unparserResult,
|
|
180
|
+
unparserContext,
|
|
181
|
+
unparserOutputCompanion,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const newPushOutput: PushOutput = value => {
|
|
185
|
+
const outputQueueWriteLaterIndex = getOutputQueueWriteLaterIndex();
|
|
186
|
+
outputQueue.splice(outputQueueWriteLaterIndex, 0, value);
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const newFinishOutput: FinishOutput = () => {
|
|
190
|
+
const outputQueueWriteLaterIndex = getOutputQueueWriteLaterIndex();
|
|
191
|
+
const [ removedValue ] = outputQueue.splice(outputQueueWriteLaterIndex, 1);
|
|
192
|
+
invariant(
|
|
193
|
+
removedValue === writeLater,
|
|
194
|
+
'WriteLater was not removed from the output queue.',
|
|
195
|
+
);
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
iteratorStack.unshift([
|
|
199
|
+
unparserResultWithoutElements[Symbol.asyncIterator](),
|
|
200
|
+
newPushOutput,
|
|
201
|
+
newFinishOutput,
|
|
202
|
+
]);
|
|
203
|
+
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
pushOutput(value);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/* eslint-disable prefer-arrow-callback */
|
|
2
|
+
|
|
3
|
+
import { Unparser, UnparserResult } from "./unparser.js";
|
|
4
|
+
import { UnparserOutputCompanion } from "./unparserOutputCompanion.js";
|
|
5
|
+
import { unparserImplementationInvariant } from "./unparserImplementationInvariant.js";
|
|
6
|
+
import { parserImplementationInvariant } from "./parserImplementationInvariant.js";
|
|
7
|
+
|
|
8
|
+
export type UnparserContext<Sequence, Element> = {
|
|
9
|
+
get position(): number;
|
|
10
|
+
|
|
11
|
+
writeLater(length: number): AsyncIterable<WriteLater<Sequence, Element>, WriteLater<Sequence, Element>>;
|
|
12
|
+
writeEarlier<Input>(
|
|
13
|
+
writeLater: WriteLater<Sequence, Element>,
|
|
14
|
+
unparser: Unparser<Input, Sequence, Element>,
|
|
15
|
+
input: Input,
|
|
16
|
+
): AsyncIterable<WriteEarlier<Sequence, Element>, void>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export class WriteLater<Sequence, Element> extends Error {
|
|
20
|
+
name = 'WriteLater';
|
|
21
|
+
|
|
22
|
+
constructor (
|
|
23
|
+
private readonly _position: number,
|
|
24
|
+
private readonly _length: number,
|
|
25
|
+
) {
|
|
26
|
+
super(`(position: ${_position}, length: ${_length})`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get position() {
|
|
30
|
+
return this._position;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
get positionEnd() {
|
|
34
|
+
return this._position + this._length;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
get length() {
|
|
38
|
+
return this._length;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class WriteEarlier<Sequence, Element> {
|
|
43
|
+
constructor(
|
|
44
|
+
public readonly writeLater: WriteLater<Sequence, Element>,
|
|
45
|
+
public readonly unparserResult: UnparserResult<Sequence, Element>,
|
|
46
|
+
public readonly unparserContext: UnparserContext<Sequence, Element>,
|
|
47
|
+
) {}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export class UnparserContextImplementation<Sequence, Element> implements UnparserContext<Sequence, Element> {
|
|
51
|
+
private _position: number;
|
|
52
|
+
|
|
53
|
+
constructor(
|
|
54
|
+
private readonly _outputCompanion: UnparserOutputCompanion<Sequence, Element>,
|
|
55
|
+
position = 0,
|
|
56
|
+
) {
|
|
57
|
+
this._position = position;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
get position() {
|
|
61
|
+
return this._position;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async * writeLater(length: number): AsyncIterable<WriteLater<Sequence, Element>, WriteLater<Sequence, Element>> {
|
|
65
|
+
parserImplementationInvariant(
|
|
66
|
+
length >= 0,
|
|
67
|
+
'Length of WriteLater must be non-negative, got %s.',
|
|
68
|
+
length,
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
const writeLater = new WriteLater<Sequence, Element>(this._position, length);
|
|
72
|
+
yield writeLater;
|
|
73
|
+
return writeLater;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async * writeEarlier<Input>(
|
|
77
|
+
writeLater: WriteLater<Sequence, Element>,
|
|
78
|
+
unparser: Unparser<Input, Sequence, Element>,
|
|
79
|
+
input: Input,
|
|
80
|
+
): AsyncIterable<WriteEarlier<Sequence, Element>, void> {
|
|
81
|
+
const unparserContext = new UnparserContextImplementation(this._outputCompanion, writeLater.position);
|
|
82
|
+
yield new WriteEarlier(
|
|
83
|
+
writeLater,
|
|
84
|
+
(async function * () {
|
|
85
|
+
const startPosition = unparserContext.position;
|
|
86
|
+
|
|
87
|
+
yield * unparser(input, unparserContext);
|
|
88
|
+
|
|
89
|
+
const endPosition = unparserContext.position;
|
|
90
|
+
const writtenLength = endPosition - startPosition;
|
|
91
|
+
|
|
92
|
+
unparserImplementationInvariant(
|
|
93
|
+
writtenLength === writeLater.length,
|
|
94
|
+
[
|
|
95
|
+
'WriteEarlier was supposed to write %s elements but wrote %s elements instead.',
|
|
96
|
+
'Corresponding WriteLater stack (this is where it was created): %s',
|
|
97
|
+
'End of corresponding WriteEarlier stack.',
|
|
98
|
+
],
|
|
99
|
+
writeLater.length,
|
|
100
|
+
writtenLength,
|
|
101
|
+
writeLater.stack,
|
|
102
|
+
);
|
|
103
|
+
})(),
|
|
104
|
+
unparserContext,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
handleYield(
|
|
109
|
+
value:
|
|
110
|
+
| Sequence
|
|
111
|
+
| WriteLater<Sequence, Element>
|
|
112
|
+
| WriteEarlier<Sequence, Element>
|
|
113
|
+
,
|
|
114
|
+
) {
|
|
115
|
+
if (value instanceof WriteEarlier) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (value instanceof WriteLater) {
|
|
120
|
+
this._position += value.length;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const length = this._outputCompanion.length(value);
|
|
125
|
+
this._position += length;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
|
|
2
|
+
export class UnparserError extends Error {
|
|
3
|
+
name = 'UnparserError';
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export class UnparserImplementationError extends UnparserError {
|
|
7
|
+
name = 'UnparserImplementationError';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class UnparserImplementationInvariantError extends UnparserImplementationError {
|
|
11
|
+
name = 'UnparserImplementationInvariantError';
|
|
12
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { UnparserImplementationInvariantError } from './unparserError.js';
|
|
2
|
+
import { type Falsy, customInvariant, type ValueOrAccessor } from './customInvariant.js';
|
|
3
|
+
|
|
4
|
+
export function unparserImplementationInvariant<T>(value: T, format: ValueOrAccessor<string | string[]>, ...formatArguments: any[]): Exclude<T, Falsy> {
|
|
5
|
+
return customInvariant(UnparserImplementationInvariantError, value, format, ...formatArguments);
|
|
6
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { StringParserInputCompanion, Uint8ArrayParserInputCompanion } from "./parserInputCompanion.js";
|
|
2
|
+
|
|
3
|
+
export type UnparserOutputCompanion<Sequence, Element> = {
|
|
4
|
+
is(value: unknown): value is Sequence;
|
|
5
|
+
from(elements: Element[]): Sequence;
|
|
6
|
+
concat(sequences: Sequence[]): Sequence;
|
|
7
|
+
length(sequence: Sequence): number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export class StringUnparserOutputCompanion extends StringParserInputCompanion implements UnparserOutputCompanion<string, string> {
|
|
11
|
+
concat(sequences: string[]): string {
|
|
12
|
+
return sequences.join('');
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const stringUnparserOutputCompanion = new StringUnparserOutputCompanion();
|
|
17
|
+
|
|
18
|
+
export class Uint8ArrayUnparserOutputCompanion extends Uint8ArrayParserInputCompanion implements UnparserOutputCompanion<Uint8Array, number> {
|
|
19
|
+
concat(sequences: Uint8Array[]): Uint8Array {
|
|
20
|
+
return Buffer.concat(sequences);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const uint8ArrayUnparserOutputCompanion = new Uint8ArrayUnparserOutputCompanion();
|
package/src/zip.ts
CHANGED
|
@@ -4,37 +4,25 @@ export type ZipCompression =
|
|
|
4
4
|
| 'deflate'
|
|
5
5
|
;
|
|
6
6
|
|
|
7
|
-
export type
|
|
8
|
-
type: 'unix';
|
|
9
|
-
unixPermissions: number;
|
|
7
|
+
export type ZipEntryAttributes = {
|
|
10
8
|
};
|
|
11
9
|
|
|
12
|
-
|
|
13
|
-
type: 'dos';
|
|
14
|
-
dosPermissions: number;
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export type ZipPermissions =
|
|
18
|
-
| ZipUnixPermissions
|
|
19
|
-
| ZipDosPermissions
|
|
20
|
-
;
|
|
21
|
-
|
|
22
|
-
export type ZipFileEntry = {
|
|
23
|
-
type: 'file';
|
|
10
|
+
type ZipEntryCommon = {
|
|
24
11
|
path: string;
|
|
25
12
|
date: Date;
|
|
26
13
|
comment: string;
|
|
27
|
-
|
|
14
|
+
hostSystem: 'unix' | 'dos';
|
|
15
|
+
attributes: ZipEntryAttributes;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export type ZipFileEntry = ZipEntryCommon & {
|
|
19
|
+
type: 'file';
|
|
28
20
|
compression: ZipCompression;
|
|
29
21
|
content: Uint8Array;
|
|
30
22
|
};
|
|
31
23
|
|
|
32
|
-
export type ZipDirectoryEntry = {
|
|
24
|
+
export type ZipDirectoryEntry = ZipEntryCommon & {
|
|
33
25
|
type: 'directory';
|
|
34
|
-
path: string;
|
|
35
|
-
date: Date;
|
|
36
|
-
comment: string;
|
|
37
|
-
permissions: ZipPermissions;
|
|
38
26
|
};
|
|
39
27
|
|
|
40
28
|
export type ZipEntry =
|
|
@@ -45,4 +33,4 @@ export type ZipEntry =
|
|
|
45
33
|
export type Zip = {
|
|
46
34
|
comment: string;
|
|
47
35
|
entries: ZipEntry[];
|
|
48
|
-
}
|
|
36
|
+
};
|
package/src/zipParser.test.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { testProp } from '@fast-check/ava';
|
|
2
2
|
import { arbitraryZipStream } from './arbitraryZipStream.js';
|
|
3
3
|
import { zipParser } from './zipParser.js';
|
|
4
|
-
import {
|
|
4
|
+
import { uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
5
5
|
import { runParser } from './parser.js';
|
|
6
6
|
|
|
7
|
-
async function * readableStreamToUint8ArrayAsyncIterator(stream: ReadableStream<Uint8Array>) {
|
|
8
|
-
for await (const chunk of stream) {
|
|
9
|
-
yield chunk;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
7
|
testProp(
|
|
14
8
|
'zip',
|
|
15
9
|
[
|
|
16
10
|
arbitraryZipStream,
|
|
17
11
|
],
|
|
18
12
|
async (t, [ zip, zipStream ]) => {
|
|
19
|
-
const actual = await runParser(zipParser,
|
|
13
|
+
const actual = await runParser(zipParser, zipStream, uint8ArrayParserInputCompanion, {
|
|
20
14
|
errorJoinMode: 'all',
|
|
21
15
|
});
|
|
22
16
|
|