@futpib/parser 1.0.1 → 1.0.2
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/allSettledStream.js +32 -14
- package/build/allSettledStream.test.js +32 -0
- package/build/androidPackage.d.ts +39 -0
- package/build/androidPackageParser.d.ts +17 -0
- package/build/androidPackageParser.js +183 -0
- package/build/{apkParser.test.js → androidPackageParser.test.js} +6 -6
- package/build/androidPackageUnparser.d.ts +4 -0
- package/build/{apkUnparser.js → androidPackageUnparser.js} +23 -23
- package/build/androidPackageUnparser.test.js +26 -0
- package/build/arbitrarilySlicedAsyncInterable.d.ts +3 -1
- package/build/arbitrarilySlicedAsyncInterable.js +3 -3
- package/build/arrayParser.test.js +3 -3
- package/build/backsmali.d.ts +1 -0
- package/build/backsmali.js +22 -0
- package/build/bsonParser.js +6 -2
- package/build/customInvariant.d.ts +1 -1
- package/build/dalvikBytecodeParser/formatParsers.d.ts +97 -0
- package/build/dalvikBytecodeParser/formatParsers.js +169 -0
- package/build/dalvikBytecodeParser.d.ts +107 -0
- package/build/dalvikBytecodeParser.js +836 -0
- package/build/dalvikExecutable.d.ts +158 -0
- package/build/dalvikExecutable.js +20 -0
- package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +4 -0
- package/build/dalvikExecutableParser/stringSyntaxParser.js +76 -0
- package/build/dalvikExecutableParser/typeParsers.d.ts +10 -0
- package/build/dalvikExecutableParser/typeParsers.js +34 -0
- package/build/dalvikExecutableParser/typedNumbers.d.ts +90 -0
- package/build/dalvikExecutableParser/typedNumbers.js +19 -0
- package/build/dalvikExecutableParser.d.ts +5 -0
- package/build/dalvikExecutableParser.js +1439 -0
- package/build/dalvikExecutableParser.test.js +70 -0
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +298 -0
- package/build/debugLogInputParser.d.ts +4 -0
- package/build/debugLogInputParser.js +16 -0
- package/build/debugLogParser.js +14 -3
- package/build/disjunctionParser.d.ts +2 -1
- package/build/disjunctionParser.js +4 -2
- package/build/elementTerminatedArrayParser.d.ts +3 -0
- package/build/elementTerminatedArrayParser.js +18 -0
- package/build/elementTerminatedArrayParser.test.js +52 -0
- package/build/elementTerminatedSequenceArrayParser.d.ts +3 -0
- package/build/elementTerminatedSequenceArrayParser.js +32 -0
- package/build/elementTerminatedSequenceArrayParser.test.js +34 -0
- package/build/elementTerminatedSequenceParser.d.ts +3 -0
- package/build/elementTerminatedSequenceParser.js +27 -0
- package/build/elementTerminatedSequenceParser.test.js +34 -0
- package/build/exactElementParser.js +10 -5
- package/build/exactSequenceParser.d.ts +2 -1
- package/build/exactSequenceParser.js +12 -2
- package/build/fetchCid.d.ts +1 -0
- package/build/fetchCid.js +107 -0
- package/build/fixedLengthSequenceParser.d.ts +1 -0
- package/build/fixedLengthSequenceParser.js +18 -1
- package/build/fixedLengthSequenceParser.test.js +41 -0
- package/build/hasExecutable.d.ts +1 -0
- package/build/hasExecutable.js +8 -0
- package/build/highResolutionTimer.d.ts +16 -0
- package/build/highResolutionTimer.js +42 -0
- package/build/inputReader.d.ts +11 -0
- package/build/inputReader.js +37 -0
- package/build/inputReader.test.js +165 -0
- package/build/inputReaderState.d.ts +10 -0
- package/build/inputReaderState.js +16 -0
- package/build/inspect.d.ts +1 -0
- package/build/inspect.js +7 -0
- package/build/javaKeyStoreParser.test.js +2 -2
- package/build/jsonParser.d.ts +2 -0
- package/build/jsonParser.js +11 -14
- package/build/leb128Parser.d.ts +7 -0
- package/build/leb128Parser.js +82 -0
- package/build/leb128Parser.test.js +107 -0
- package/build/lookaheadParser.d.ts +2 -0
- package/build/lookaheadParser.js +14 -0
- package/build/negativeLookaheadParser.js +21 -15
- package/build/negativeLookaheadParser.test.js +30 -0
- package/build/nonEmptyArrayParser.d.ts +2 -0
- package/build/nonEmptyArrayParser.js +32 -0
- package/build/nonEmptyArrayParser.test.js +16 -0
- package/build/parser.d.ts +10 -1
- package/build/parser.js +66 -31
- package/build/parser.test.js +79 -12
- package/build/parserAccessorParser.js +9 -1
- package/build/parserConsumedSequenceParser.js +20 -15
- package/build/parserContext.d.ts +14 -0
- package/build/parserContext.js +56 -27
- package/build/parserContext.test.js +27 -0
- package/build/parserCreatorCompose.d.ts +1 -0
- package/build/parserCreatorCompose.js +8 -2
- package/build/parserError.d.ts +6 -0
- package/build/parserError.js +6 -6
- package/build/parserInputCompanion.d.ts +15 -0
- package/build/parserInputCompanion.js +38 -0
- package/build/promiseCompose.d.ts +1 -1
- package/build/promiseCompose.js +12 -1
- package/build/promiseSettled.d.ts +1 -0
- package/build/promiseSettled.js +4 -0
- package/build/separatedArrayParser.d.ts +2 -0
- package/build/separatedArrayParser.js +39 -0
- package/build/separatedArrayParser.test.d.ts +1 -0
- package/build/separatedArrayParser.test.js +21 -0
- package/build/sequenceBuffer.d.ts +10 -0
- package/build/sequenceBuffer.js +54 -2
- package/build/sequenceBuffer.test.js +57 -0
- package/build/sequenceTerminatedSequenceParser.d.ts +5 -0
- package/build/sequenceTerminatedSequenceParser.js +32 -0
- package/build/sequenceTerminatedSequenceParser.test.d.ts +1 -0
- package/build/sequenceTerminatedSequenceParser.test.js +37 -0
- package/build/skipParser.d.ts +1 -1
- package/build/skipParser.js +4 -2
- package/build/skipToParser.d.ts +2 -0
- package/build/skipToParser.js +11 -0
- package/build/sliceBoundedParser.d.ts +1 -1
- package/build/sliceBoundedParser.js +7 -2
- package/build/sliceBoundedParser.test.js +35 -1
- package/build/smali.d.ts +1 -0
- package/build/smali.js +17 -0
- package/build/smaliParser.d.ts +12 -0
- package/build/smaliParser.js +656 -0
- package/build/smaliParser.test.d.ts +1 -0
- package/build/smaliParser.test.js +115 -0
- package/build/terminatedArrayParser.d.ts +3 -1
- package/build/terminatedArrayParser.js +79 -2
- package/build/terminatedArrayParser.test.d.ts +1 -0
- package/build/terminatedArrayParser.test.js +131 -0
- package/build/toAsyncIterable.d.ts +1 -0
- package/build/toAsyncIterable.js +7 -0
- package/build/toAsyncIterator.d.ts +1 -0
- package/build/toAsyncIterator.js +33 -0
- package/build/tupleParser.d.ts +4 -0
- package/build/tupleParser.js +1 -5
- package/build/unionParser.d.ts +2 -1
- package/build/unionParser.js +27 -12
- package/build/unionParser.test.d.ts +1 -0
- package/build/unionParser.test.js +60 -0
- package/build/zipParser.d.ts +7 -2
- package/build/zipParser.js +36 -12
- package/build/zipUnparser.d.ts +4 -1
- package/build/zipUnparser.js +55 -26
- package/build/zipUnparser.test.js +14 -14
- package/package.json +23 -7
- package/src/allSettledStream.test.ts +40 -0
- package/src/allSettledStream.ts +47 -15
- package/src/androidPackage.ts +48 -0
- package/src/{apkParser.test.ts → androidPackageParser.test.ts} +6 -7
- package/src/{apkParser.test.ts.md → androidPackageParser.test.ts.md} +4 -4
- package/src/androidPackageParser.test.ts.snap +0 -0
- package/src/androidPackageParser.ts +440 -0
- package/src/androidPackageUnparser.test.ts +36 -0
- package/src/androidPackageUnparser.ts +120 -0
- package/src/arbitrarilySlicedAsyncInterable.ts +7 -2
- package/src/arrayParser.test.ts +3 -3
- package/src/backsmali.ts +30 -0
- package/src/bsonParser.ts +13 -2
- package/src/customInvariant.ts +1 -1
- package/src/dalvikBytecodeParser/formatParsers.ts +421 -0
- package/src/dalvikBytecodeParser.ts +2074 -0
- package/src/dalvikExecutable.ts +220 -0
- package/src/dalvikExecutableParser/stringSyntaxParser.ts +145 -0
- package/src/dalvikExecutableParser/typeParsers.ts +65 -0
- package/src/dalvikExecutableParser/typedNumbers.ts +57 -0
- package/src/dalvikExecutableParser.test.ts +89 -0
- package/src/dalvikExecutableParser.test.ts.md +634 -0
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +2768 -0
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +346 -0
- package/src/debugLogInputParser.ts +28 -0
- package/src/debugLogParser.ts +19 -3
- package/src/disjunctionParser.ts +10 -5
- package/src/elementTerminatedArrayParser.test.ts +99 -0
- package/src/elementTerminatedArrayParser.ts +31 -0
- package/src/elementTerminatedSequenceArrayParser.test.ts +54 -0
- package/src/elementTerminatedSequenceArrayParser.ts +52 -0
- package/src/elementTerminatedSequenceParser.test.ts +54 -0
- package/src/elementTerminatedSequenceParser.ts +43 -0
- package/src/exactElementParser.ts +17 -11
- package/src/exactSequenceParser.ts +23 -2
- package/src/fetchCid.ts +125 -0
- package/src/fixedLengthSequenceParser.test.ts +77 -0
- package/src/fixedLengthSequenceParser.ts +28 -1
- package/src/hasExecutable.ts +11 -0
- package/src/highResolutionTimer.ts +49 -0
- package/src/inputReader.test.ts +209 -0
- package/src/inputReader.ts +75 -0
- package/src/inputReaderState.ts +33 -0
- package/src/inspect.ts +9 -0
- package/src/javaKeyStoreParser.test.ts +2 -3
- package/src/jsonParser.ts +13 -25
- package/src/leb128Parser.test.ts +171 -0
- package/src/leb128Parser.ts +125 -0
- package/src/lookaheadParser.ts +19 -0
- package/src/negativeLookaheadParser.test.ts +53 -0
- package/src/negativeLookaheadParser.ts +26 -14
- package/src/nonEmptyArrayParser.test.ts +20 -0
- package/src/nonEmptyArrayParser.ts +44 -0
- package/src/optionalParser.ts +1 -0
- package/src/parser.test.ts +131 -12
- package/src/parser.test.ts.md +21 -21
- package/src/parser.test.ts.snap +0 -0
- package/src/parser.ts +149 -45
- package/src/parserAccessorParser.ts +12 -2
- package/src/parserConsumedSequenceParser.ts +25 -16
- package/src/parserContext.test.ts +31 -0
- package/src/parserContext.ts +109 -37
- package/src/parserCreatorCompose.ts +20 -2
- package/src/parserError.ts +9 -6
- package/src/parserInputCompanion.ts +55 -0
- package/src/promiseCompose.ts +17 -3
- package/src/promiseSettled.ts +6 -0
- package/src/separatedArrayParser.test.ts +34 -0
- package/src/separatedArrayParser.ts +55 -0
- package/src/sequenceBuffer.test.ts +70 -0
- package/src/sequenceBuffer.ts +88 -2
- package/src/sequenceTerminatedSequenceParser.test.ts +60 -0
- package/src/sequenceTerminatedSequenceParser.ts +62 -0
- package/src/skipParser.ts +7 -5
- package/src/skipToParser.ts +16 -0
- package/src/sliceBoundedParser.test.ts +43 -1
- package/src/sliceBoundedParser.ts +19 -1
- package/src/smali.ts +24 -0
- package/src/smaliParser.test.ts +132 -0
- package/src/smaliParser.test.ts.md +2320 -0
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +1166 -0
- package/src/terminatedArrayParser.test.ts +258 -0
- package/src/terminatedArrayParser.ts +108 -3
- package/src/toAsyncIterable.ts +7 -0
- package/src/toAsyncIterator.ts +48 -0
- package/src/tupleParser.ts +8 -5
- package/src/unionParser.test.ts +79 -0
- package/src/unionParser.ts +44 -16
- package/src/zipParser.ts +77 -18
- package/src/zipUnparser.test.ts +18 -18
- package/src/zipUnparser.ts +88 -27
- package/build/apk.d.ts +0 -39
- package/build/apkParser.d.ts +0 -16
- package/build/apkParser.js +0 -164
- package/build/apkUnparser.d.ts +0 -4
- package/build/apkUnparser.test.js +0 -26
- package/build/arbitraryDosDate.d.ts +0 -2
- package/build/arbitraryDosDate.js +0 -8
- package/build/arbitraryZipEntry.d.ts +0 -3
- package/build/arbitraryZipEntry.js +0 -26
- package/build/createDisjunctionParser.d.ts +0 -2
- package/build/createDisjunctionParser.js +0 -47
- package/build/createExactParser.d.ts +0 -2
- package/build/createExactParser.js +0 -12
- package/build/createSequentialUnionParser.d.ts +0 -2
- package/build/createSequentialUnionParser.js +0 -69
- package/build/fixedLengthChunkParser.d.ts +0 -2
- package/build/fixedLengthChunkParser.js +0 -12
- package/build/fixedLengthParser.d.ts +0 -2
- package/build/fixedLengthParser.js +0 -12
- package/build/inputChunkBuffer.d.ts +0 -15
- package/build/inputChunkBuffer.js +0 -40
- package/build/inputChunkBuffer.test.js +0 -34
- package/build/inputCompanion.d.ts +0 -18
- package/build/inputCompanion.js +0 -28
- package/build/invariantDefined.d.ts +0 -1
- package/build/invariantDefined.js +0 -5
- package/build/invariantIdentity.d.ts +0 -3
- package/build/invariantIdentity.js +0 -5
- package/build/javaKeystoreParser.d.ts +0 -2
- package/build/javaKeystoreParser.js +0 -7
- package/build/jsonParser2.d.ts +0 -3
- package/build/jsonParser2.js +0 -52
- package/build/jsonParser2.test.js +0 -22
- package/build/negativeLookahead.d.ts +0 -2
- package/build/negativeLookahead.js +0 -18
- package/build/parserCompose.d.ts +0 -3
- package/build/parserCompose.js +0 -7
- package/build/parserImplementationInvariantInvariant.d.ts +0 -3
- package/build/parserImplementationInvariantInvariant.js +0 -15
- package/build/parserInvariant.d.ts +0 -4
- package/build/parserInvariant.js +0 -11
- package/build/promiseFish.d.ts +0 -1
- package/build/promiseFish.js +0 -3
- package/build/sequenceParser.d.ts +0 -3
- package/build/sequenceParser.js +0 -10
- package/build/terminatedSequenceParser.d.ts +0 -2
- package/build/terminatedSequenceParser.js +0 -24
- package/build/unparserInputCompanion.d.ts +0 -15
- package/build/unparserInputCompanion.js +0 -13
- package/build/zipEntry.d.ts +0 -28
- package/build/zipFile.d.ts +0 -32
- package/build/zipFileEntry.d.ts +0 -6
- package/src/apk.ts +0 -48
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +0 -392
- package/src/apkUnparser.test.ts +0 -37
- package/src/apkUnparser.ts +0 -120
- package/src/invariantDefined.ts +0 -6
- package/src/invariantIdentity.ts +0 -8
- /package/build/{apk.js → androidPackage.js} +0 -0
- /package/build/{apkParser.test.d.ts → androidPackageParser.test.d.ts} +0 -0
- /package/build/{apkUnparser.test.d.ts → androidPackageUnparser.test.d.ts} +0 -0
- /package/build/{arbitraryDosPermissions.d.ts → dalvikExecutableParser.test.d.ts} +0 -0
- /package/build/{arbitraryDosPermissions.js → dalvikExecutableParserAgainstSmaliParser.test.d.ts} +0 -0
- /package/build/{inputChunkBuffer.test.d.ts → elementTerminatedArrayParser.test.d.ts} +0 -0
- /package/build/{jsonParser2.test.d.ts → elementTerminatedSequenceArrayParser.test.d.ts} +0 -0
- /package/build/{parserParsingInvariant.d.ts → elementTerminatedSequenceParser.test.d.ts} +0 -0
- /package/build/{parserParsingInvariant.js → fixedLengthSequenceParser.test.d.ts} +0 -0
- /package/build/{zipEntry.js → leb128Parser.test.d.ts} +0 -0
- /package/build/{zipFile.js → negativeLookaheadParser.test.d.ts} +0 -0
- /package/build/{zipFileEntry.js → nonEmptyArrayParser.test.d.ts} +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import * as fc from 'fast-check';
|
|
3
|
+
import { testProp } from '@fast-check/ava';
|
|
4
|
+
import { createTerminatedArrayParser, createTerminatedArrayParserNaive, createTerminatedArrayParserUnsafe } from './terminatedArrayParser.js';
|
|
5
|
+
import { Parser, runParser } from './parser.js';
|
|
6
|
+
import { stringParserInputCompanion, uint8ArrayParserInputCompanion } from './parserInputCompanion.js';
|
|
7
|
+
import { HighResolutionTotalTimer } from './highResolutionTimer.js';
|
|
8
|
+
import { createElementParser } from './elementParser.js';
|
|
9
|
+
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
10
|
+
import { createTupleParser } from './tupleParser.js';
|
|
11
|
+
import { createNegativeLookaheadParser } from './negativeLookaheadParser.js';
|
|
12
|
+
import { promiseCompose } from './promiseCompose.js';
|
|
13
|
+
import { createUnionParser } from './unionParser.js';
|
|
14
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
15
|
+
|
|
16
|
+
test('terminatedArrayParser of union parsers', async t => {
|
|
17
|
+
const parser: Parser<[ number[], number ], Uint8Array> = createTerminatedArrayParser(
|
|
18
|
+
createUnionParser([
|
|
19
|
+
createExactElementParser(1),
|
|
20
|
+
createExactElementParser(2),
|
|
21
|
+
]),
|
|
22
|
+
createExactElementParser(0),
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
const input = new Uint8Array([ 0 ]);
|
|
26
|
+
|
|
27
|
+
const result = await runParser(parser, input, uint8ArrayParserInputCompanion);
|
|
28
|
+
|
|
29
|
+
t.deepEqual(result, [ [], 0 ]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
testProp(
|
|
33
|
+
'terminatedArrayParser both terminator and element matching',
|
|
34
|
+
[
|
|
35
|
+
fc.string(),
|
|
36
|
+
],
|
|
37
|
+
async (t, string) => {
|
|
38
|
+
const terminatedArrayParserNaive = createTerminatedArrayParserNaive(
|
|
39
|
+
createExactSequenceParser(string),
|
|
40
|
+
createExactSequenceParser(string),
|
|
41
|
+
);
|
|
42
|
+
const terminatedArrayParser = createTerminatedArrayParser(
|
|
43
|
+
createExactSequenceParser(string),
|
|
44
|
+
createExactSequenceParser(string),
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
const [
|
|
48
|
+
naiveResult,
|
|
49
|
+
result,
|
|
50
|
+
] = await Promise.allSettled([
|
|
51
|
+
runParser(
|
|
52
|
+
terminatedArrayParserNaive,
|
|
53
|
+
string,
|
|
54
|
+
stringParserInputCompanion,
|
|
55
|
+
),
|
|
56
|
+
runParser(
|
|
57
|
+
terminatedArrayParser,
|
|
58
|
+
string,
|
|
59
|
+
stringParserInputCompanion,
|
|
60
|
+
),
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
t.is(naiveResult.status, 'rejected');
|
|
64
|
+
|
|
65
|
+
if (
|
|
66
|
+
result.status === 'rejected'
|
|
67
|
+
&& naiveResult.status === 'rejected'
|
|
68
|
+
) {
|
|
69
|
+
result.reason.message = 'REDACTED';
|
|
70
|
+
naiveResult.reason.message = 'REDACTED';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
t.deepEqual(result, naiveResult);
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
verbose: true,
|
|
77
|
+
},
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
const naiveTotalTimer = new HighResolutionTotalTimer();
|
|
81
|
+
const optimizedTotalTimer = new HighResolutionTotalTimer();
|
|
82
|
+
const unsafeTotalTimer = new HighResolutionTotalTimer();
|
|
83
|
+
|
|
84
|
+
testProp.serial(
|
|
85
|
+
'terminatedArrayParser',
|
|
86
|
+
[
|
|
87
|
+
fc
|
|
88
|
+
.integer({
|
|
89
|
+
min: 1,
|
|
90
|
+
max: 2 ** 8,
|
|
91
|
+
})
|
|
92
|
+
.chain(terminatorLength => (
|
|
93
|
+
fc
|
|
94
|
+
.string({
|
|
95
|
+
minLength: terminatorLength,
|
|
96
|
+
})
|
|
97
|
+
.map(string => ({
|
|
98
|
+
string,
|
|
99
|
+
terminator: string.slice(0 - terminatorLength)
|
|
100
|
+
}))
|
|
101
|
+
.filter(({ string, terminator }) => string.split(terminator).length === 2)
|
|
102
|
+
))
|
|
103
|
+
],
|
|
104
|
+
async (t, { string, terminator }) => {
|
|
105
|
+
const terminatedArrayParserNaive = createTerminatedArrayParserNaive(
|
|
106
|
+
promiseCompose(
|
|
107
|
+
createTupleParser([
|
|
108
|
+
createNegativeLookaheadParser(createExactSequenceParser(terminator)),
|
|
109
|
+
createElementParser<string>(),
|
|
110
|
+
]),
|
|
111
|
+
([ _, element ]) => element,
|
|
112
|
+
),
|
|
113
|
+
createExactSequenceParser(terminator),
|
|
114
|
+
);
|
|
115
|
+
const terminatedArrayParser = createTerminatedArrayParser(
|
|
116
|
+
promiseCompose(
|
|
117
|
+
createTupleParser([
|
|
118
|
+
createNegativeLookaheadParser(createExactSequenceParser(terminator)),
|
|
119
|
+
createElementParser<string>(),
|
|
120
|
+
]),
|
|
121
|
+
([ _, element ]) => element,
|
|
122
|
+
),
|
|
123
|
+
createExactSequenceParser(terminator),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
const createTestWrapperParser = (innerParser: typeof terminatedArrayParser): Parser<{
|
|
127
|
+
string: string;
|
|
128
|
+
terminator: string;
|
|
129
|
+
position: number;
|
|
130
|
+
}, string> => async parserContext => {
|
|
131
|
+
const [ characters, terminator ] = await innerParser(parserContext);
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
string: characters.join(''),
|
|
135
|
+
terminator,
|
|
136
|
+
nextPeek: await parserContext.peek(0),
|
|
137
|
+
position: parserContext.position,
|
|
138
|
+
};
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const actualNaive = await naiveTotalTimer.measureAsync(() => runParser(
|
|
142
|
+
createTestWrapperParser(terminatedArrayParserNaive),
|
|
143
|
+
string,
|
|
144
|
+
stringParserInputCompanion,
|
|
145
|
+
));
|
|
146
|
+
|
|
147
|
+
t.is(actualNaive.string.length + actualNaive.terminator.length, string.length);
|
|
148
|
+
|
|
149
|
+
const actual = await optimizedTotalTimer.measureAsync(() => runParser(
|
|
150
|
+
createTestWrapperParser(terminatedArrayParser),
|
|
151
|
+
string,
|
|
152
|
+
stringParserInputCompanion,
|
|
153
|
+
));
|
|
154
|
+
|
|
155
|
+
t.deepEqual(actual, actualNaive);
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
verbose: true,
|
|
159
|
+
},
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
test.serial(
|
|
163
|
+
'terminatedArrayParser performance',
|
|
164
|
+
t => {
|
|
165
|
+
t.true(
|
|
166
|
+
optimizedTotalTimer.time * 1n < naiveTotalTimer.time,
|
|
167
|
+
`Naive: ${naiveTotalTimer.time / 1000000n}ms, Optimized: ${optimizedTotalTimer.time / 1000000n}ms`,
|
|
168
|
+
);
|
|
169
|
+
},
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
testProp.serial(
|
|
173
|
+
'terminatedArrayParserUnsafe',
|
|
174
|
+
[
|
|
175
|
+
fc
|
|
176
|
+
.integer({
|
|
177
|
+
min: 1,
|
|
178
|
+
max: 2 ** 8,
|
|
179
|
+
})
|
|
180
|
+
.chain(terminatorLength => (
|
|
181
|
+
fc
|
|
182
|
+
.string({
|
|
183
|
+
minLength: terminatorLength,
|
|
184
|
+
})
|
|
185
|
+
.map(string => ({
|
|
186
|
+
string,
|
|
187
|
+
terminator: string.slice(0 - terminatorLength)
|
|
188
|
+
}))
|
|
189
|
+
.filter(({ string, terminator }) => string.split(terminator).length === 2)
|
|
190
|
+
))
|
|
191
|
+
],
|
|
192
|
+
async (t, { string, terminator }) => {
|
|
193
|
+
const terminatedArrayParserNaive = createTerminatedArrayParserNaive(
|
|
194
|
+
promiseCompose(
|
|
195
|
+
createTupleParser([
|
|
196
|
+
createNegativeLookaheadParser(createExactSequenceParser(terminator)),
|
|
197
|
+
createElementParser<string>(),
|
|
198
|
+
]),
|
|
199
|
+
([ _, element ]) => element,
|
|
200
|
+
),
|
|
201
|
+
createExactSequenceParser(terminator),
|
|
202
|
+
);
|
|
203
|
+
const terminatedArrayParserUnsafe = createTerminatedArrayParserUnsafe(
|
|
204
|
+
promiseCompose(
|
|
205
|
+
createTupleParser([
|
|
206
|
+
createNegativeLookaheadParser(createExactSequenceParser(terminator)),
|
|
207
|
+
createElementParser<string>(),
|
|
208
|
+
]),
|
|
209
|
+
([ _, element ]) => element,
|
|
210
|
+
),
|
|
211
|
+
createExactSequenceParser(terminator),
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const createTestWrapperParser = (innerParser: typeof terminatedArrayParserUnsafe): Parser<{
|
|
215
|
+
string: string;
|
|
216
|
+
terminator: string;
|
|
217
|
+
position: number;
|
|
218
|
+
}, string> => async parserContext => {
|
|
219
|
+
const [ characters, terminator ] = await innerParser(parserContext);
|
|
220
|
+
|
|
221
|
+
return {
|
|
222
|
+
string: characters.join(''),
|
|
223
|
+
terminator,
|
|
224
|
+
nextPeek: await parserContext.peek(0),
|
|
225
|
+
position: parserContext.position,
|
|
226
|
+
};
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const actualNaive = await runParser(
|
|
230
|
+
createTestWrapperParser(terminatedArrayParserNaive),
|
|
231
|
+
string,
|
|
232
|
+
stringParserInputCompanion,
|
|
233
|
+
);
|
|
234
|
+
|
|
235
|
+
t.is(actualNaive.string.length + actualNaive.terminator.length, string.length);
|
|
236
|
+
|
|
237
|
+
const actual = await unsafeTotalTimer.measureAsync(() => runParser(
|
|
238
|
+
createTestWrapperParser(terminatedArrayParserUnsafe),
|
|
239
|
+
string,
|
|
240
|
+
stringParserInputCompanion,
|
|
241
|
+
));
|
|
242
|
+
|
|
243
|
+
t.deepEqual(actual, actualNaive);
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
verbose: true,
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
test.serial(
|
|
251
|
+
'terminatedArrayParserUnsafe performance',
|
|
252
|
+
t => {
|
|
253
|
+
t.true(
|
|
254
|
+
unsafeTotalTimer.time * 1n < naiveTotalTimer.time,
|
|
255
|
+
`Naive: ${naiveTotalTimer.time / 1000000n}ms, Unsafe: ${unsafeTotalTimer.time / 1000000n}ms`,
|
|
256
|
+
);
|
|
257
|
+
},
|
|
258
|
+
);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { getParserName, type Parser, setParserName } from './parser.js';
|
|
2
|
+
import { ParserParsingFailedError } from './parserError.js';
|
|
3
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
2
4
|
import { promiseCompose } from './promiseCompose.js';
|
|
3
5
|
import { createUnionParser } from './unionParser.js';
|
|
4
6
|
|
|
@@ -8,9 +10,9 @@ class Terminated<T> {
|
|
|
8
10
|
) {}
|
|
9
11
|
}
|
|
10
12
|
|
|
11
|
-
export const
|
|
13
|
+
export const createTerminatedArrayParserNaive = <ElementOutput, TerminatorOutput, Sequence>(
|
|
12
14
|
elementParser: Parser<ElementOutput, Sequence>,
|
|
13
|
-
terminatorParser: Parser<
|
|
15
|
+
terminatorParser: Parser<TerminatorOutput, Sequence>,
|
|
14
16
|
): Parser<[ElementOutput[], TerminatorOutput], Sequence> => {
|
|
15
17
|
const wrappedTerminatorParser = promiseCompose(terminatorParser, terminatorValue => new Terminated(terminatorValue));
|
|
16
18
|
|
|
@@ -24,7 +26,7 @@ export const createTerminatedArrayParser = <ElementOutput, TerminatorOutput, Seq
|
|
|
24
26
|
promiseCompose(terminatorParser, terminatorValue => new Terminated(terminatorValue)),
|
|
25
27
|
]);
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
const terminatedArrayParserNaive: Parser<[ElementOutput[], TerminatorOutput], Sequence> = async parserContext => {
|
|
28
30
|
const elements: ElementOutput[] = [];
|
|
29
31
|
|
|
30
32
|
while (true) {
|
|
@@ -37,4 +39,107 @@ export const createTerminatedArrayParser = <ElementOutput, TerminatorOutput, Seq
|
|
|
37
39
|
elements.push(elementOrTerminator);
|
|
38
40
|
}
|
|
39
41
|
};
|
|
42
|
+
|
|
43
|
+
setParserName(terminatedArrayParserNaive, `${getParserName(elementParser, 'anonymousElement')}*?${getParserName(terminatorParser, 'anonymousTerminator')}`);
|
|
44
|
+
|
|
45
|
+
return terminatedArrayParserNaive;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const createTerminatedArrayParser = <ElementOutput, TerminatorOutput, Sequence>(
|
|
49
|
+
elementParser: Parser<ElementOutput, Sequence>,
|
|
50
|
+
terminatorParser: Parser<TerminatorOutput, Sequence>,
|
|
51
|
+
): Parser<[ElementOutput[], TerminatorOutput], Sequence> => {
|
|
52
|
+
const terminatedArrayParser: Parser<[ElementOutput[], TerminatorOutput], Sequence> = async parserContext => {
|
|
53
|
+
const elements: ElementOutput[] = [];
|
|
54
|
+
|
|
55
|
+
while (true) {
|
|
56
|
+
const terminatorParserContext = parserContext.lookahead({
|
|
57
|
+
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const terminatorValue = await terminatorParser(terminatorParserContext);
|
|
62
|
+
|
|
63
|
+
const elementParserContext = parserContext.lookahead({
|
|
64
|
+
debugName: getParserName(elementParser, 'anonymousElement'),
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
try {
|
|
68
|
+
await elementParser(elementParserContext);
|
|
69
|
+
|
|
70
|
+
parserImplementationInvariant(
|
|
71
|
+
false,
|
|
72
|
+
[
|
|
73
|
+
'Both element and terminator parsers matched.',
|
|
74
|
+
'Element parser: %s',
|
|
75
|
+
'Terminator parser: %s',
|
|
76
|
+
],
|
|
77
|
+
getParserName(elementParser, 'anonymousElement'),
|
|
78
|
+
getParserName(terminatorParser, 'anonymousTerminator'),
|
|
79
|
+
);
|
|
80
|
+
} catch (error) {
|
|
81
|
+
if (!(error instanceof ParserParsingFailedError)) {
|
|
82
|
+
throw error;
|
|
83
|
+
}
|
|
84
|
+
} finally {
|
|
85
|
+
elementParserContext.dispose();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
terminatorParserContext.unlookahead();
|
|
89
|
+
|
|
90
|
+
return [ elements, terminatorValue ];
|
|
91
|
+
} catch (error) {
|
|
92
|
+
if (!(error instanceof ParserParsingFailedError)) {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
} finally {
|
|
96
|
+
terminatorParserContext.dispose();
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const element = await elementParser(parserContext);
|
|
100
|
+
|
|
101
|
+
elements.push(element);
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
setParserName(terminatedArrayParser, `${getParserName(elementParser, 'anonymousElement')}*?${getParserName(terminatorParser, 'anonymousTerminator')}`);
|
|
106
|
+
|
|
107
|
+
return terminatedArrayParser;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
export const createTerminatedArrayParserUnsafe = <ElementOutput, TerminatorOutput, Sequence>(
|
|
111
|
+
elementParser: Parser<ElementOutput, Sequence>,
|
|
112
|
+
terminatorParser: Parser<TerminatorOutput, Sequence>,
|
|
113
|
+
): Parser<[ElementOutput[], TerminatorOutput], Sequence> => {
|
|
114
|
+
const terminatedArrayParserUnsafe: Parser<[ElementOutput[], TerminatorOutput], Sequence> = async parserContext => {
|
|
115
|
+
const elements: ElementOutput[] = [];
|
|
116
|
+
|
|
117
|
+
while (true) {
|
|
118
|
+
const terminatorParserContext = parserContext.lookahead({
|
|
119
|
+
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const terminatorValue = await terminatorParser(terminatorParserContext);
|
|
124
|
+
|
|
125
|
+
terminatorParserContext.unlookahead();
|
|
126
|
+
|
|
127
|
+
return [ elements, terminatorValue ];
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (!(error instanceof ParserParsingFailedError)) {
|
|
130
|
+
throw error;
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
terminatorParserContext.dispose();
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const element = await elementParser(parserContext);
|
|
137
|
+
|
|
138
|
+
elements.push(element);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
setParserName(terminatedArrayParserUnsafe, `${getParserName(elementParser, 'anonymousElement')}*?${getParserName(terminatorParser, 'anonymousTerminator')}`);
|
|
143
|
+
|
|
144
|
+
return terminatedArrayParserUnsafe;
|
|
40
145
|
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import invariant from "invariant";
|
|
2
|
+
|
|
3
|
+
function isAsyncIterable<T>(value: any): value is AsyncIterable<T> {
|
|
4
|
+
return value && typeof value[Symbol.asyncIterator] === 'function';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function isIterable<T>(value: any): value is Iterable<T> {
|
|
8
|
+
return value && typeof value[Symbol.iterator] === 'function';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function isIterator<T>(value: any): value is Iterator<T> {
|
|
12
|
+
return value && typeof value.next === 'function';
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function iteratorToAsyncIterator<T>(iterator: Iterator<T>): AsyncIterator<T> {
|
|
16
|
+
return {
|
|
17
|
+
next: async () => iterator.next(),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function toAsyncIterator<T>(value: AsyncIterator<T> | AsyncIterable<T> | Iterable<T> | T): AsyncIterator<T> {
|
|
22
|
+
if (
|
|
23
|
+
typeof value === 'string'
|
|
24
|
+
|| value instanceof Uint8Array
|
|
25
|
+
) {
|
|
26
|
+
return (async function * () {
|
|
27
|
+
yield value as any;
|
|
28
|
+
})();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isAsyncIterable(value)) {
|
|
32
|
+
return value[Symbol.asyncIterator]();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (isIterable(value)) {
|
|
36
|
+
return iteratorToAsyncIterator(value[Symbol.iterator]());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (isIterator<T>(value)) {
|
|
40
|
+
return iteratorToAsyncIterator(value);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
invariant(
|
|
44
|
+
false,
|
|
45
|
+
'Value must be an async iterator, async iterable, iterable or iterator got %s.',
|
|
46
|
+
value,
|
|
47
|
+
);
|
|
48
|
+
}
|
package/src/tupleParser.ts
CHANGED
|
@@ -10,6 +10,10 @@ export function createTupleParser<A, B, C, D, E, F, G, H, Sequence>([ parserA, p
|
|
|
10
10
|
export function createTupleParser<A, B, C, D, E, F, G, H, I, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I], Sequence>;
|
|
11
11
|
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J], Sequence>;
|
|
12
12
|
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, K, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ, parserK ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>, Parser<K, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J, K], Sequence>;
|
|
13
|
+
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, K, L, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ, parserK, parserL ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>, Parser<K, Sequence>, Parser<L, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J, K, L], Sequence>;
|
|
14
|
+
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, K, L, M, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ, parserK, parserL, parserM ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>, Parser<K, Sequence>, Parser<L, Sequence>, Parser<M, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J, K, L, M], Sequence>;
|
|
15
|
+
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, K, L, M, N, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ, parserK, parserL, parserM, parserN ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>, Parser<K, Sequence>, Parser<L, Sequence>, Parser<M, Sequence>, Parser<N, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J, K, L, M, N], Sequence>;
|
|
16
|
+
export function createTupleParser<A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, Sequence>([ parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH, parserI, parserJ, parserK, parserL, parserM, parserN, parserO ]: [Parser<A, Sequence>, Parser<B, Sequence>, Parser<C, Sequence>, Parser<D, Sequence>, Parser<E, Sequence>, Parser<F, Sequence>, Parser<G, Sequence>, Parser<H, Sequence>, Parser<I, Sequence>, Parser<J, Sequence>, Parser<K, Sequence>, Parser<L, Sequence>, Parser<M, Sequence>, Parser<N, Sequence>, Parser<O, Sequence>]): Parser<[A, B, C, D, E, F, G, H, I, J, K, L, M, N, O], Sequence>;
|
|
13
17
|
export function createTupleParser<Sequence>(parsers: Array<Parser<unknown, Sequence>>): Parser<unknown[], Sequence> {
|
|
14
18
|
const tupleParser: Parser<unknown[], Sequence> = async parserContext => {
|
|
15
19
|
const values: unknown[] = [];
|
|
@@ -23,11 +27,10 @@ export function createTupleParser<Sequence>(parsers: Array<Parser<unknown, Seque
|
|
|
23
27
|
return values;
|
|
24
28
|
};
|
|
25
29
|
|
|
26
|
-
setParserName(
|
|
27
|
-
|
|
28
|
-
parsers.map(parser => getParserName(parser, 'anonymousTupleChild')).join('
|
|
29
|
-
|
|
30
|
-
].join(''));
|
|
30
|
+
setParserName(
|
|
31
|
+
tupleParser,
|
|
32
|
+
parsers.map(parser => '(' + getParserName(parser, 'anonymousTupleChild') + ')').join(''),
|
|
33
|
+
);
|
|
31
34
|
|
|
32
35
|
return tupleParser;
|
|
33
36
|
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { createUnionParser } from './unionParser.js';
|
|
3
|
+
import { Parser, runParser, setParserName } from './parser.js';
|
|
4
|
+
import { stringParserInputCompanion } from './parserInputCompanion.js';
|
|
5
|
+
import { createArrayParser } from './arrayParser.js';
|
|
6
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
7
|
+
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
8
|
+
import { ParserError } from './parserError.js';
|
|
9
|
+
|
|
10
|
+
test('union of union of union', async t => {
|
|
11
|
+
const parser: Parser<string, string> = createUnionParser([
|
|
12
|
+
createExactElementParser('a'),
|
|
13
|
+
createUnionParser([
|
|
14
|
+
createExactElementParser('b'),
|
|
15
|
+
createUnionParser([
|
|
16
|
+
createExactElementParser('c'),
|
|
17
|
+
createExactElementParser('d'),
|
|
18
|
+
]),
|
|
19
|
+
createExactElementParser('e'),
|
|
20
|
+
]),
|
|
21
|
+
createExactElementParser('f'),
|
|
22
|
+
createUnionParser([
|
|
23
|
+
createExactElementParser('g'),
|
|
24
|
+
createExactElementParser('h'),
|
|
25
|
+
]),
|
|
26
|
+
]);
|
|
27
|
+
|
|
28
|
+
for (const character of 'abcdefgh') {
|
|
29
|
+
t.deepEqual(await runParser(parser, character, stringParserInputCompanion), character);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test('sync and async child parsers', async t => {
|
|
34
|
+
const parser = createArrayParser(
|
|
35
|
+
createUnionParser<string, string>([
|
|
36
|
+
async parserContext => {
|
|
37
|
+
parserContext.invariant(
|
|
38
|
+
parserContext.position % 2 === 0,
|
|
39
|
+
'Expected an even position.',
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return parserContext.read(0);
|
|
43
|
+
},
|
|
44
|
+
parserContext => {
|
|
45
|
+
parserContext.invariant(
|
|
46
|
+
parserContext.position % 2 === 1,
|
|
47
|
+
'Expected an odd position.',
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
parserContext.skip(1);
|
|
51
|
+
|
|
52
|
+
return String.fromCodePoint('A'.codePointAt(0)! + parserContext.position - 1);
|
|
53
|
+
},
|
|
54
|
+
]),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
const result = await runParser(parser, 'a?c?', stringParserInputCompanion, {
|
|
58
|
+
errorJoinMode: 'all',
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
t.deepEqual(result, 'aBcD'.split(''));
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('multiple parsers succeeded error', async t => {
|
|
65
|
+
const parser = createUnionParser([
|
|
66
|
+
setParserName(createExactSequenceParser('foo'), 'foo1'),
|
|
67
|
+
setParserName(createExactSequenceParser('foo'), 'foo2'),
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
const error = await t.throwsAsync(runParser(parser, 'foo', stringParserInputCompanion, {
|
|
71
|
+
errorJoinMode: 'all',
|
|
72
|
+
}), {
|
|
73
|
+
instanceOf: ParserError,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
t.true(error.message.includes('foo'));
|
|
77
|
+
t.true(error.message.includes('foo1'));
|
|
78
|
+
t.true(error.message.includes('foo2'));
|
|
79
|
+
});
|
package/src/unionParser.ts
CHANGED
|
@@ -3,36 +3,60 @@ import { getParserName, setParserName, type Parser } from './parser.js';
|
|
|
3
3
|
import { type ParserContext } from './parserContext.js';
|
|
4
4
|
import { ParserParsingFailedError } from './parserError.js';
|
|
5
5
|
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
6
|
+
import { DeriveSequenceElement } from './sequence.js';
|
|
7
|
+
|
|
8
|
+
const bigintReplacer = (_key: string, value: unknown) => {
|
|
9
|
+
if (typeof value === 'bigint') {
|
|
10
|
+
return `${value}n`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
6
15
|
|
|
7
16
|
export const createUnionParser = <
|
|
8
17
|
Output,
|
|
9
18
|
Sequence,
|
|
19
|
+
Element = DeriveSequenceElement<Sequence>,
|
|
10
20
|
>(
|
|
11
|
-
childParsers: Array<Parser<
|
|
12
|
-
): Parser<Output, Sequence,
|
|
21
|
+
childParsers: Array<Parser<unknown, Sequence, Element>>,
|
|
22
|
+
): Parser<Output, Sequence, Element> => {
|
|
13
23
|
parserImplementationInvariant(childParsers.length > 0, 'Union parser must have at least one child parser.');
|
|
14
24
|
|
|
15
|
-
|
|
16
|
-
|
|
25
|
+
type TaskContext = {
|
|
26
|
+
childParser: Parser<unknown, Sequence, Element>,
|
|
27
|
+
childParserContext: ParserContext<Sequence, Element>,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const unionParser: Parser<Output, Sequence, Element> = async parserContext => {
|
|
31
|
+
let runningChildParserContexts: TaskContext[] = [];
|
|
17
32
|
|
|
18
|
-
const
|
|
33
|
+
const createChildParserTask = (childParser: Parser<unknown, Sequence, Element>) => {
|
|
19
34
|
const childParserContext = parserContext.lookahead({
|
|
20
35
|
debugName: getParserName(childParser, 'anonymousUnionChild'),
|
|
21
36
|
});
|
|
22
37
|
|
|
23
|
-
|
|
38
|
+
const context: TaskContext = {
|
|
39
|
+
childParser,
|
|
40
|
+
childParserContext,
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
runningChildParserContexts.push(context);
|
|
44
|
+
|
|
45
|
+
const getChildParserPromise = (async () => childParser(childParserContext) as Promise<Output>);
|
|
24
46
|
|
|
25
|
-
const promise =
|
|
47
|
+
const promise = getChildParserPromise();
|
|
26
48
|
|
|
27
49
|
return {
|
|
28
50
|
promise,
|
|
29
|
-
context
|
|
51
|
+
context,
|
|
30
52
|
};
|
|
31
|
-
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const childParserResults = allSettledStream<Output, TaskContext>(childParsers.map(createChildParserTask));
|
|
32
56
|
|
|
33
57
|
const parserParsingFailedErrors: ParserParsingFailedError[] = [];
|
|
34
58
|
const successfulParserOutputs: Output[] = [];
|
|
35
|
-
const
|
|
59
|
+
const successfulTaskContexts: TaskContext[] = [];
|
|
36
60
|
let didUnlookahead = false;
|
|
37
61
|
|
|
38
62
|
for await (const childParserResult of childParserResults) {
|
|
@@ -42,7 +66,7 @@ export const createUnionParser = <
|
|
|
42
66
|
|
|
43
67
|
if (childParserResult.status === 'fulfilled') {
|
|
44
68
|
successfulParserOutputs.push(childParserResult.value);
|
|
45
|
-
|
|
69
|
+
successfulTaskContexts.push(childParserResult.context);
|
|
46
70
|
} else {
|
|
47
71
|
const error = childParserResult.reason;
|
|
48
72
|
|
|
@@ -60,7 +84,7 @@ export const createUnionParser = <
|
|
|
60
84
|
parserImplementationInvariant(!didUnlookahead, 'Union parser unlookaheaded multiple times.');
|
|
61
85
|
didUnlookahead = true;
|
|
62
86
|
const [ runningChildParserContext ] = runningChildParserContexts;
|
|
63
|
-
runningChildParserContext.unlookahead();
|
|
87
|
+
runningChildParserContext.childParserContext.unlookahead();
|
|
64
88
|
}
|
|
65
89
|
}
|
|
66
90
|
|
|
@@ -71,8 +95,12 @@ export const createUnionParser = <
|
|
|
71
95
|
'Successful parser outputs, indented, separated by newlines:',
|
|
72
96
|
'%s',
|
|
73
97
|
'End of successful parser outputs.',
|
|
98
|
+
'Successful parser names, indented, separated by newlines:',
|
|
99
|
+
'%s',
|
|
100
|
+
'End of successful parser names.',
|
|
74
101
|
],
|
|
75
|
-
() => successfulParserOutputs.map(output => ' ' + JSON.stringify(output)).join('\n'),
|
|
102
|
+
() => successfulParserOutputs.map(output => ' ' + JSON.stringify(output, bigintReplacer)).join('\n'),
|
|
103
|
+
() => successfulTaskContexts.map(taskContext => ' ' + getParserName(taskContext.childParser, 'anonymousUnionChild')).join('\n'),
|
|
76
104
|
);
|
|
77
105
|
|
|
78
106
|
parserContext.invariantJoin(
|
|
@@ -81,13 +109,13 @@ export const createUnionParser = <
|
|
|
81
109
|
'No union child parser succeeded.',
|
|
82
110
|
);
|
|
83
111
|
|
|
84
|
-
const [ successfulParserContext ] =
|
|
112
|
+
const [ successfulParserContext ] = successfulTaskContexts;
|
|
85
113
|
|
|
86
114
|
if (!didUnlookahead) {
|
|
87
|
-
successfulParserContext.unlookahead();
|
|
115
|
+
successfulParserContext.childParserContext.unlookahead();
|
|
88
116
|
}
|
|
89
117
|
|
|
90
|
-
successfulParserContext.dispose();
|
|
118
|
+
successfulParserContext.childParserContext.dispose();
|
|
91
119
|
|
|
92
120
|
const [ successfulParserOutput ] = successfulParserOutputs;
|
|
93
121
|
|