@futpib/parser 1.0.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/.editorconfig +12 -0
- package/.gitattributes +2 -0
- package/.github/workflows/main.yml +29 -0
- package/.yarn/releases/yarn-4.5.3.cjs +934 -0
- package/.yarnrc.yml +7 -0
- package/build/allSettledStream.d.ts +17 -0
- package/build/allSettledStream.js +21 -0
- package/build/allSettledStream.test.d.ts +1 -0
- package/build/allSettledStream.test.js +56 -0
- package/build/apk.d.ts +13 -0
- package/build/apk.js +1 -0
- package/build/apkParser.d.ts +3 -0
- package/build/apkParser.js +135 -0
- package/build/apkParser.test.d.ts +1 -0
- package/build/apkParser.test.js +22 -0
- package/build/arbitrarilySlicedAsyncInterable.d.ts +2 -0
- package/build/arbitrarilySlicedAsyncInterable.js +46 -0
- package/build/arbitrarilySlicedAsyncInterator.d.ts +2 -0
- package/build/arbitrarilySlicedAsyncInterator.js +17 -0
- package/build/arbitraryDosDate.d.ts +2 -0
- package/build/arbitraryDosDate.js +8 -0
- package/build/arbitraryDosDateTime.d.ts +2 -0
- package/build/arbitraryDosDateTime.js +9 -0
- package/build/arbitraryDosPermissions.d.ts +1 -0
- package/build/arbitraryDosPermissions.js +1 -0
- package/build/arbitraryFileSystemEntry.d.ts +14 -0
- package/build/arbitraryFileSystemEntry.js +12 -0
- package/build/arbitraryZip.d.ts +6 -0
- package/build/arbitraryZip.js +88 -0
- package/build/arbitraryZipEntry.d.ts +3 -0
- package/build/arbitraryZipEntry.js +26 -0
- package/build/arbitraryZipPermissions.d.ts +8 -0
- package/build/arbitraryZipPermissions.js +16 -0
- package/build/arbitraryZipStream.d.ts +5 -0
- package/build/arbitraryZipStream.js +50 -0
- package/build/arrayParser.d.ts +2 -0
- package/build/arrayParser.js +30 -0
- package/build/arrayParser.test.d.ts +1 -0
- package/build/arrayParser.test.js +9 -0
- package/build/bsonParser.d.ts +3 -0
- package/build/bsonParser.js +70 -0
- package/build/bsonParser.test.d.ts +1 -0
- package/build/bsonParser.test.js +24 -0
- package/build/createDisjunctionParser.d.ts +2 -0
- package/build/createDisjunctionParser.js +47 -0
- package/build/createExactParser.d.ts +2 -0
- package/build/createExactParser.js +12 -0
- package/build/createSequentialUnionParser.d.ts +2 -0
- package/build/createSequentialUnionParser.js +69 -0
- package/build/debugLogParser.d.ts +2 -0
- package/build/debugLogParser.js +23 -0
- package/build/disjunctionParser.d.ts +2 -0
- package/build/disjunctionParser.js +35 -0
- package/build/elementParser.d.ts +3 -0
- package/build/elementParser.js +1 -0
- package/build/endOfInputParser.d.ts +4 -0
- package/build/endOfInputParser.js +4 -0
- package/build/exactElementParser.d.ts +3 -0
- package/build/exactElementParser.js +6 -0
- package/build/exactSequenceParser.d.ts +2 -0
- package/build/exactSequenceParser.js +15 -0
- package/build/fixedLengthChunkParser.d.ts +2 -0
- package/build/fixedLengthChunkParser.js +12 -0
- package/build/fixedLengthParser.d.ts +2 -0
- package/build/fixedLengthParser.js +12 -0
- package/build/fixedLengthSequenceParser.d.ts +2 -0
- package/build/fixedLengthSequenceParser.js +16 -0
- package/build/index.d.ts +18 -0
- package/build/index.js +17 -0
- package/build/index.test.d.ts +1 -0
- package/build/index.test.js +2 -0
- package/build/inputChunkBuffer.d.ts +15 -0
- package/build/inputChunkBuffer.js +40 -0
- package/build/inputChunkBuffer.test.d.ts +1 -0
- package/build/inputChunkBuffer.test.js +34 -0
- package/build/inputCompanion.d.ts +18 -0
- package/build/inputCompanion.js +28 -0
- package/build/inputReader.d.ts +22 -0
- package/build/inputReader.js +105 -0
- package/build/inputReader.test.d.ts +1 -0
- package/build/inputReader.test.js +174 -0
- package/build/invariantDefined.d.ts +1 -0
- package/build/invariantDefined.js +5 -0
- package/build/invariantIdentity.d.ts +3 -0
- package/build/invariantIdentity.js +5 -0
- package/build/jsonParser.d.ts +3 -0
- package/build/jsonParser.js +133 -0
- package/build/jsonParser.test.d.ts +1 -0
- package/build/jsonParser.test.js +17 -0
- package/build/jsonParser2.d.ts +3 -0
- package/build/jsonParser2.js +52 -0
- package/build/jsonParser2.test.d.ts +1 -0
- package/build/jsonParser2.test.js +22 -0
- package/build/listParser.d.ts +2 -0
- package/build/listParser.js +34 -0
- package/build/negativeLookahead.d.ts +2 -0
- package/build/negativeLookahead.js +20 -0
- package/build/optionalParser.d.ts +2 -0
- package/build/optionalParser.js +23 -0
- package/build/parser.d.ts +10 -0
- package/build/parser.js +53 -0
- package/build/parser.test.d.ts +1 -0
- package/build/parser.test.js +126 -0
- package/build/parserAccessorParser.d.ts +2 -0
- package/build/parserAccessorParser.js +1 -0
- package/build/parserCompose.d.ts +3 -0
- package/build/parserCompose.js +7 -0
- package/build/parserContext.d.ts +48 -0
- package/build/parserContext.js +193 -0
- package/build/parserContext.test.d.ts +1 -0
- package/build/parserContext.test.js +115 -0
- package/build/parserCreatorCompose.d.ts +3 -0
- package/build/parserCreatorCompose.js +10 -0
- package/build/parserError.d.ts +43 -0
- package/build/parserError.js +61 -0
- package/build/parserImplementationInvariant.d.ts +2 -0
- package/build/parserImplementationInvariant.js +5 -0
- package/build/parserImplementationInvariantInvariant.d.ts +3 -0
- package/build/parserImplementationInvariantInvariant.js +15 -0
- package/build/parserInvariant.d.ts +4 -0
- package/build/parserInvariant.js +11 -0
- package/build/parserParsingInvariant.d.ts +1 -0
- package/build/parserParsingInvariant.js +1 -0
- package/build/promiseCompose.d.ts +1 -0
- package/build/promiseCompose.js +3 -0
- package/build/promiseFish.d.ts +1 -0
- package/build/promiseFish.js +3 -0
- package/build/sequence.d.ts +1 -0
- package/build/sequence.js +1 -0
- package/build/sequenceBuffer.d.ts +15 -0
- package/build/sequenceBuffer.js +40 -0
- package/build/sequenceBuffer.test.d.ts +1 -0
- package/build/sequenceBuffer.test.js +34 -0
- package/build/sequenceParser.d.ts +3 -0
- package/build/sequenceParser.js +10 -0
- package/build/skipParser.d.ts +2 -0
- package/build/skipParser.js +8 -0
- package/build/sliceBoundedParser.d.ts +2 -0
- package/build/sliceBoundedParser.js +24 -0
- package/build/sliceBoundedParser.test.d.ts +1 -0
- package/build/sliceBoundedParser.test.js +24 -0
- package/build/terminatedArrayParser.d.ts +2 -0
- package/build/terminatedArrayParser.js +27 -0
- package/build/terminatedSequenceParser.d.ts +2 -0
- package/build/terminatedSequenceParser.js +24 -0
- package/build/tupleParser.d.ts +11 -0
- package/build/tupleParser.js +17 -0
- package/build/unionParser.d.ts +2 -0
- package/build/unionParser.js +68 -0
- package/build/zip.d.ts +31 -0
- package/build/zip.js +1 -0
- package/build/zipEntry.d.ts +28 -0
- package/build/zipEntry.js +1 -0
- package/build/zipFile.d.ts +32 -0
- package/build/zipFile.js +1 -0
- package/build/zipFileEntry.d.ts +6 -0
- package/build/zipFileEntry.js +1 -0
- package/build/zipParser.d.ts +70 -0
- package/build/zipParser.js +255 -0
- package/build/zipParser.test.d.ts +1 -0
- package/build/zipParser.test.js +22 -0
- package/package.json +52 -0
- package/readme.md +17 -0
- package/renovate.json +6 -0
- package/src/allSettledStream.test.ts +59 -0
- package/src/allSettledStream.test.ts.md +52 -0
- package/src/allSettledStream.test.ts.snap +0 -0
- package/src/allSettledStream.ts +44 -0
- package/src/apk.ts +16 -0
- package/src/apkParser.test.ts +30 -0
- package/src/apkParser.test.ts.md +268 -0
- package/src/apkParser.test.ts.snap +0 -0
- package/src/apkParser.ts +327 -0
- package/src/arbitrarilySlicedAsyncInterable.ts +73 -0
- package/src/arbitrarilySlicedAsyncInterator.ts +25 -0
- package/src/arbitraryDosDateTime.ts +10 -0
- package/src/arbitraryFileSystemEntry.ts +36 -0
- package/src/arbitraryZip.ts +132 -0
- package/src/arbitraryZipPermissions.ts +25 -0
- package/src/arbitraryZipStream.ts +60 -0
- package/src/arrayParser.test.ts +11 -0
- package/src/arrayParser.ts +35 -0
- package/src/bsonParser.test.ts +37 -0
- package/src/bsonParser.ts +131 -0
- package/src/debugLogParser.ts +51 -0
- package/src/disjunctionParser.ts +55 -0
- package/src/elementParser.ts +4 -0
- package/src/endOfInputParser.ts +11 -0
- package/src/exactElementParser.ts +20 -0
- package/src/exactSequenceParser.ts +27 -0
- package/src/fixedLengthSequenceParser.ts +24 -0
- package/src/index.test.ts +3 -0
- package/src/index.ts +76 -0
- package/src/inputCompanion.ts +43 -0
- package/src/inputReader.test.ts +238 -0
- package/src/inputReader.ts +159 -0
- package/src/invariantDefined.ts +6 -0
- package/src/invariantIdentity.ts +8 -0
- package/src/jsonParser.test.ts +26 -0
- package/src/jsonParser.ts +225 -0
- package/src/listParser.ts +52 -0
- package/src/negativeLookahead.ts +26 -0
- package/src/optionalParser.ts +27 -0
- package/src/parser.test.ts +176 -0
- package/src/parser.test.ts.md +68 -0
- package/src/parser.test.ts.snap +0 -0
- package/src/parser.ts +103 -0
- package/src/parserAccessorParser.ts +3 -0
- package/src/parserContext.test.ts +154 -0
- package/src/parserContext.ts +316 -0
- package/src/parserCreatorCompose.ts +23 -0
- package/src/parserError.ts +85 -0
- package/src/parserImplementationInvariant.ts +6 -0
- package/src/parserInvariant.ts +26 -0
- package/src/promiseCompose.ts +7 -0
- package/src/sequence.ts +10 -0
- package/src/sequenceBuffer.test.ts +50 -0
- package/src/sequenceBuffer.ts +58 -0
- package/src/skipParser.ts +12 -0
- package/src/sliceBoundedParser.test.ts +28 -0
- package/src/sliceBoundedParser.ts +30 -0
- package/src/terminatedArrayParser.ts +40 -0
- package/src/tupleParser.ts +33 -0
- package/src/unionParser.ts +104 -0
- package/src/zip.ts +48 -0
- package/src/zipParser.test.ts +31 -0
- package/src/zipParser.ts +501 -0
- package/tsconfig.json +109 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getParserName, setParserName } from "./parser.js";
|
|
2
|
+
import { ParserParsingFailedError } from "./parserError.js";
|
|
3
|
+
export const createArrayParser = (elementParser) => {
|
|
4
|
+
const arrayParser = async (parserContext) => {
|
|
5
|
+
const elements = [];
|
|
6
|
+
while (true) {
|
|
7
|
+
const elementParserContext = parserContext.lookahead();
|
|
8
|
+
const initialPosition = elementParserContext.position;
|
|
9
|
+
try {
|
|
10
|
+
const element = await elementParser(elementParserContext);
|
|
11
|
+
if (elementParserContext.position === initialPosition) {
|
|
12
|
+
return elements;
|
|
13
|
+
}
|
|
14
|
+
elements.push(element);
|
|
15
|
+
elementParserContext.unlookahead();
|
|
16
|
+
}
|
|
17
|
+
catch (error) {
|
|
18
|
+
if (error instanceof ParserParsingFailedError) {
|
|
19
|
+
return elements;
|
|
20
|
+
}
|
|
21
|
+
throw error;
|
|
22
|
+
}
|
|
23
|
+
finally {
|
|
24
|
+
elementParserContext.dispose();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
setParserName(arrayParser, getParserName(elementParser, 'anonymousArrayChild') + '*');
|
|
29
|
+
return arrayParser;
|
|
30
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { createArrayParser } from './arrayParser.js';
|
|
3
|
+
import { runParser } from './parser.js';
|
|
4
|
+
import { stringInputCompanion } from './inputCompanion.js';
|
|
5
|
+
test('does not loop forever with a child parser that does not consume anything', async (t) => {
|
|
6
|
+
const parser = createArrayParser(async () => undefined);
|
|
7
|
+
const result = await runParser(parser, 'foo', stringInputCompanion);
|
|
8
|
+
t.deepEqual(result, []);
|
|
9
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { getParserName, setParserName } from './parser.js';
|
|
2
|
+
import { invariantDefined } from './invariantDefined.js';
|
|
3
|
+
import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js';
|
|
4
|
+
import { promiseCompose } from './promiseCompose.js';
|
|
5
|
+
import { createTupleParser } from './tupleParser.js';
|
|
6
|
+
import { createSkipParser } from './skipParser.js';
|
|
7
|
+
import { createParserAccessorParser } from './parserAccessorParser.js';
|
|
8
|
+
import { createTerminatedArrayParser } from './terminatedArrayParser.js';
|
|
9
|
+
import { createElementParser } from './elementParser.js';
|
|
10
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
11
|
+
import { createUnionParser } from './unionParser.js';
|
|
12
|
+
import { parserCreatorCompose } from './parserCreatorCompose.js';
|
|
13
|
+
const createFixedLengthBufferParser = (length) => promiseCompose(createFixedLengthSequenceParser(length), sequence => Buffer.from(sequence));
|
|
14
|
+
const buffer1Parser = createFixedLengthBufferParser(1);
|
|
15
|
+
const buffer4Parser = createFixedLengthBufferParser(4);
|
|
16
|
+
const buffer8Parser = createFixedLengthBufferParser(8);
|
|
17
|
+
const elementParser = createElementParser();
|
|
18
|
+
const nullByteParser = createExactElementParser(0);
|
|
19
|
+
const cstringParser = promiseCompose(createTerminatedArrayParser(parserCreatorCompose(() => elementParser, (byte) => async (parserContext) => parserContext.invariant(byte, 'Expected non-null byte'))(), nullByteParser), ([sequence]) => Buffer.from(sequence).toString('utf8'));
|
|
20
|
+
const doubleParser = promiseCompose(buffer8Parser, buffer => buffer.readDoubleLE(0));
|
|
21
|
+
const int32Parser = promiseCompose(buffer4Parser, buffer => buffer.readInt32LE(0));
|
|
22
|
+
const uint32Parser = promiseCompose(buffer4Parser, buffer => buffer.readUInt32LE(0));
|
|
23
|
+
const createFixedLengthStringParser = (length) => promiseCompose(createFixedLengthBufferParser(length), buffer => buffer.toString('utf8'));
|
|
24
|
+
const createFixedLengthNullTerminatedStringParser = (lengthWihoutNullTerminator) => promiseCompose(createTupleParser([
|
|
25
|
+
createFixedLengthStringParser(lengthWihoutNullTerminator),
|
|
26
|
+
nullByteParser,
|
|
27
|
+
]), ([string]) => string);
|
|
28
|
+
const bsonStringParser = async (parserContext) => {
|
|
29
|
+
const stringSize = await uint32Parser(parserContext);
|
|
30
|
+
return createFixedLengthNullTerminatedStringParser(stringSize - 1)(parserContext);
|
|
31
|
+
};
|
|
32
|
+
const bsonArrayParser = promiseCompose(createTupleParser([
|
|
33
|
+
createSkipParser(4),
|
|
34
|
+
createParserAccessorParser(() => bsonElementListParser),
|
|
35
|
+
]), ([_, elements]) => elements.map(([_, value]) => value));
|
|
36
|
+
const bsonBooleanParser = async (parserContext) => {
|
|
37
|
+
const booleanValue = invariantDefined(await parserContext.read(0), 'Unexpected end of input');
|
|
38
|
+
return booleanValue === 1;
|
|
39
|
+
};
|
|
40
|
+
const int8Parser = promiseCompose(buffer1Parser, buffer => buffer.readInt8(0));
|
|
41
|
+
const createExactInt8Parser = (value) => parserCreatorCompose(() => int8Parser, actualValue => async (parserContext) => {
|
|
42
|
+
parserContext.invariant(actualValue === value, 'Expected %s, got %s', value, actualValue);
|
|
43
|
+
return actualValue;
|
|
44
|
+
})();
|
|
45
|
+
const createBsonElementParser = (elementType, valueParser) => setParserName(promiseCompose(createTupleParser([
|
|
46
|
+
createExactInt8Parser(elementType),
|
|
47
|
+
cstringParser,
|
|
48
|
+
valueParser,
|
|
49
|
+
]), ([_, elementName, elementValue]) => [elementName, elementValue]), `bsonElementParser(${elementType}, ${getParserName(valueParser)})`);
|
|
50
|
+
const bsonDoubleElementParser = createBsonElementParser(1, doubleParser);
|
|
51
|
+
const bsonStringElementParser = createBsonElementParser(2, bsonStringParser);
|
|
52
|
+
const bsonDocumentElementParser = createBsonElementParser(3, createParserAccessorParser(() => bsonDocumentParser));
|
|
53
|
+
const bsonArrayElementParser = createBsonElementParser(4, bsonArrayParser);
|
|
54
|
+
const bsonBooleanElementParser = createBsonElementParser(8, bsonBooleanParser);
|
|
55
|
+
const bsonNullElementParser = createBsonElementParser(10, async () => null);
|
|
56
|
+
const bsonInt32ElementParser = createBsonElementParser(16, int32Parser);
|
|
57
|
+
const bsonElementParser = createUnionParser([
|
|
58
|
+
bsonDoubleElementParser,
|
|
59
|
+
bsonStringElementParser,
|
|
60
|
+
bsonDocumentElementParser,
|
|
61
|
+
bsonArrayElementParser,
|
|
62
|
+
bsonBooleanElementParser,
|
|
63
|
+
bsonNullElementParser,
|
|
64
|
+
bsonInt32ElementParser,
|
|
65
|
+
]);
|
|
66
|
+
const bsonElementListParser = promiseCompose(createTerminatedArrayParser(bsonElementParser, nullByteParser), ([elements]) => elements);
|
|
67
|
+
export const bsonDocumentParser = promiseCompose(createTupleParser([
|
|
68
|
+
createSkipParser(4),
|
|
69
|
+
bsonElementListParser,
|
|
70
|
+
]), ([_, elements]) => Object.fromEntries(elements));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { testProp, fc } from '@fast-check/ava';
|
|
2
|
+
import { BSON } from 'bson';
|
|
3
|
+
import { bsonDocumentParser } from './bsonParser.js';
|
|
4
|
+
import { arbitrarilySlicedAsyncIterator } from './arbitrarilySlicedAsyncInterator.js';
|
|
5
|
+
import { runParser } from './parser.js';
|
|
6
|
+
import { uint8ArrayInputCompanion } from './inputCompanion.js';
|
|
7
|
+
testProp('bson', [
|
|
8
|
+
arbitrarilySlicedAsyncIterator(fc
|
|
9
|
+
.json()
|
|
10
|
+
.filter(jsonString => {
|
|
11
|
+
// BSON.serialize does not support non-object top-level values
|
|
12
|
+
const jsonValue = JSON.parse(jsonString);
|
|
13
|
+
return (jsonValue !== null
|
|
14
|
+
&& typeof jsonValue === 'object'
|
|
15
|
+
&& !Array.isArray(jsonValue));
|
|
16
|
+
})
|
|
17
|
+
.map(jsonString => BSON.serialize(JSON.parse(jsonString)))),
|
|
18
|
+
], async (t, [bsonUint8Array, bsonUint8ArrayChunkIterator]) => {
|
|
19
|
+
const expected = BSON.deserialize(bsonUint8Array);
|
|
20
|
+
const actual = await runParser(bsonDocumentParser, bsonUint8ArrayChunkIterator, uint8ArrayInputCompanion);
|
|
21
|
+
t.deepEqual(actual, expected);
|
|
22
|
+
}, {
|
|
23
|
+
verbose: true,
|
|
24
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { ParserParsingFailedError } from './parserError.js';
|
|
2
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
3
|
+
import { parserParsingInvariant } from './parserParsingInvariant.js';
|
|
4
|
+
function getParserName(parser) {
|
|
5
|
+
return parser.name || 'anonymousDisjunctionChild';
|
|
6
|
+
}
|
|
7
|
+
export const createDisjunctionParser = (childParsers) => {
|
|
8
|
+
parserImplementationInvariant(childParsers.length > 0, 'Disjunction parser must have at least one child parser.');
|
|
9
|
+
const disjunctionParser = async (parserContext) => {
|
|
10
|
+
const parserParsingFailedErrors = [];
|
|
11
|
+
for (const childParser of childParsers) {
|
|
12
|
+
const childParserContext = parserContext.lookahead(getParserName(childParser));
|
|
13
|
+
const [childParserResult] = await Promise.allSettled([childParser(childParserContext)]);
|
|
14
|
+
if (childParserResult.status === 'fulfilled') {
|
|
15
|
+
const successfulParserOutput = childParserResult.value;
|
|
16
|
+
childParserContext.unlookahead();
|
|
17
|
+
childParserContext.dispose();
|
|
18
|
+
return successfulParserOutput;
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const error = childParserResult.reason;
|
|
22
|
+
if (error instanceof ParserParsingFailedError) {
|
|
23
|
+
parserParsingFailedErrors.push(error);
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
parserParsingInvariant(false, [
|
|
31
|
+
'No disjunction child parser succeeded.',
|
|
32
|
+
'Parsing errors, indented, separated by newlines:',
|
|
33
|
+
'%s',
|
|
34
|
+
'End of parsing errors.',
|
|
35
|
+
], parserParsingFailedErrors
|
|
36
|
+
.flatMap(error => error.stack?.split('\n'))
|
|
37
|
+
.map(line => ' ' + line)
|
|
38
|
+
.join('\n'));
|
|
39
|
+
};
|
|
40
|
+
const name = [
|
|
41
|
+
'(',
|
|
42
|
+
...childParsers.map(getParserName).join('|'),
|
|
43
|
+
')',
|
|
44
|
+
].join('');
|
|
45
|
+
Object.defineProperty(disjunctionParser, 'name', { value: name });
|
|
46
|
+
return disjunctionParser;
|
|
47
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
2
|
+
export const createExactParser = (inputChunk) => {
|
|
3
|
+
parserImplementationInvariant(length > 0, 'Length must be positive.');
|
|
4
|
+
return async (parserContext) => {
|
|
5
|
+
const elements = [];
|
|
6
|
+
for (let i = 0; i < length; i++) {
|
|
7
|
+
const element = await parserContext.read(0);
|
|
8
|
+
elements.push(element);
|
|
9
|
+
}
|
|
10
|
+
return parserContext.from(elements);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { ParserParsingFailedError } from './parserError.js';
|
|
2
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
3
|
+
import { parserParsingInvariant } from './parserParsingInvariant.js';
|
|
4
|
+
function getParserName(parser) {
|
|
5
|
+
return parser.name || 'anonymousSequentialUnionChild';
|
|
6
|
+
}
|
|
7
|
+
export const createDisjunctionParser = (childParsers) => {
|
|
8
|
+
parserImplementationInvariant(childParsers.length > 0, 'SequentialUnion parser must have at least one child parser.');
|
|
9
|
+
const sequentialUnionParser = async (parserContext) => {
|
|
10
|
+
let runningChildParserContexts = [];
|
|
11
|
+
const childParserResults = (async function* () {
|
|
12
|
+
for (const childParser of childParsers) {
|
|
13
|
+
const childParserContext = parserContext.lookahead(getParserName(childParser));
|
|
14
|
+
runningChildParserContexts.push(childParserContext);
|
|
15
|
+
const [promiseSettledResult] = await Promise.allSettled([childParser(childParserContext)]);
|
|
16
|
+
yield {
|
|
17
|
+
...promiseSettledResult,
|
|
18
|
+
context: childParserContext,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
22
|
+
const parserParsingFailedErrors = [];
|
|
23
|
+
const successfulParserOutputs = [];
|
|
24
|
+
const successfulParserContexts = [];
|
|
25
|
+
for await (const childParserResult of childParserResults) {
|
|
26
|
+
runningChildParserContexts = runningChildParserContexts.filter(runningChildParserContext => runningChildParserContext !== childParserResult.context);
|
|
27
|
+
if (childParserResult.status === 'fulfilled') {
|
|
28
|
+
successfulParserOutputs.push(childParserResult.value);
|
|
29
|
+
successfulParserContexts.push(childParserResult.context);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
const error = childParserResult.reason;
|
|
33
|
+
if (error instanceof ParserParsingFailedError) {
|
|
34
|
+
parserParsingFailedErrors.push(error);
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
parserImplementationInvariant(successfulParserOutputs.length <= 1, [
|
|
42
|
+
'Multiple sequentialUnion child parsers succeeded.',
|
|
43
|
+
'Successful parser outputs, indented, separated by newlines:',
|
|
44
|
+
'%s',
|
|
45
|
+
'End of successful parser outputs.',
|
|
46
|
+
], successfulParserOutputs.map(output => ' ' + JSON.stringify(output)).join('\n'));
|
|
47
|
+
parserParsingInvariant(successfulParserOutputs.length === 1, [
|
|
48
|
+
'No sequentialUnion child parser succeeded.',
|
|
49
|
+
'Parsing errors, indented, separated by newlines:',
|
|
50
|
+
'%s',
|
|
51
|
+
'End of parsing errors.',
|
|
52
|
+
], parserParsingFailedErrors
|
|
53
|
+
.flatMap(error => error.stack?.split('\n'))
|
|
54
|
+
.map(line => ' ' + line)
|
|
55
|
+
.join('\n'));
|
|
56
|
+
const [successfulParserContext] = successfulParserContexts;
|
|
57
|
+
successfulParserContext.unlookahead();
|
|
58
|
+
successfulParserContext.dispose();
|
|
59
|
+
const [successfulParserOutput] = successfulParserOutputs;
|
|
60
|
+
return successfulParserOutput;
|
|
61
|
+
};
|
|
62
|
+
const name = [
|
|
63
|
+
'(',
|
|
64
|
+
...childParsers.map(getParserName).join('|'),
|
|
65
|
+
')',
|
|
66
|
+
].join('');
|
|
67
|
+
Object.defineProperty(sequentialUnionParser, 'name', { value: name });
|
|
68
|
+
return sequentialUnionParser;
|
|
69
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { getParserName, setParserName } from "./parser.js";
|
|
2
|
+
export const createDebugLogParser = (childParser) => {
|
|
3
|
+
let idCounter = 0;
|
|
4
|
+
const debugLogParser = async (parserContext) => {
|
|
5
|
+
const id = idCounter++;
|
|
6
|
+
const initialPosition = parserContext.position;
|
|
7
|
+
console.debug('%s %s: started (position: %s)', getParserName(childParser), id, initialPosition);
|
|
8
|
+
try {
|
|
9
|
+
const result = await childParser(parserContext);
|
|
10
|
+
console.debug('%s %s: finished (position: %s, consumed: %s): %o', getParserName(childParser), id, parserContext.position, parserContext.position - initialPosition, result);
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
console.debug('%s %s: failed (position: %s, consumed: %s): %o', getParserName(childParser), id, parserContext.position, parserContext.position - initialPosition, error);
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
return setParserName(debugLogParser, [
|
|
19
|
+
'debugLog(',
|
|
20
|
+
getParserName(childParser),
|
|
21
|
+
')',
|
|
22
|
+
].join(''));
|
|
23
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { getParserName, setParserName } from './parser.js';
|
|
2
|
+
import { ParserParsingFailedError } from './parserError.js';
|
|
3
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
4
|
+
export const createDisjunctionParser = (childParsers) => {
|
|
5
|
+
parserImplementationInvariant(childParsers.length > 0, 'Disjunction parser must have at least one child parser.');
|
|
6
|
+
const disjunctionParser = async (parserContext) => {
|
|
7
|
+
const parserParsingFailedErrors = [];
|
|
8
|
+
for (const childParser of childParsers) {
|
|
9
|
+
const childParserContext = parserContext.lookahead({
|
|
10
|
+
debugName: getParserName(childParser, 'anonymousDisjunctionChild'),
|
|
11
|
+
});
|
|
12
|
+
const [childParserResult] = await Promise.allSettled([childParser(childParserContext)]);
|
|
13
|
+
if (childParserResult.status === 'fulfilled') {
|
|
14
|
+
const successfulParserOutput = childParserResult.value;
|
|
15
|
+
childParserContext.unlookahead();
|
|
16
|
+
childParserContext.dispose();
|
|
17
|
+
return successfulParserOutput;
|
|
18
|
+
}
|
|
19
|
+
const error = childParserResult.reason;
|
|
20
|
+
if (error instanceof ParserParsingFailedError) {
|
|
21
|
+
parserParsingFailedErrors.push(error);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
throw error;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
parserContext.invariantJoin(false, parserParsingFailedErrors, 'No disjunction child parser succeeded.');
|
|
28
|
+
};
|
|
29
|
+
const name = [
|
|
30
|
+
'(',
|
|
31
|
+
...childParsers.map(childParser => getParserName(childParser, 'anonymousDiscjunctionChild')).join('|'),
|
|
32
|
+
')',
|
|
33
|
+
].join('');
|
|
34
|
+
return setParserName(disjunctionParser, name);
|
|
35
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const createElementParser = () => parserContext => parserContext.read(0);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Parser } from "./parser.js";
|
|
2
|
+
import { DeriveSequenceElement } from "./sequence.js";
|
|
3
|
+
export declare const createEndOfInputParser: <Sequence, Element = DeriveSequenceElement<Sequence>>() => Parser<void, Sequence, Element>;
|
|
4
|
+
export declare const endOfInputParser: Parser<void, any, any>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const createExactElementParser = (element) => async (parserContext) => {
|
|
2
|
+
const actualElement = await parserContext.peek(0);
|
|
3
|
+
parserContext.invariant(actualElement === element, 'Expected %s, got %s', element, actualElement);
|
|
4
|
+
parserContext.skip(1);
|
|
5
|
+
return element;
|
|
6
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { inspect } from 'util';
|
|
2
|
+
import { setParserName } from './parser.js';
|
|
3
|
+
export const createExactSequenceParser = (sequence) => {
|
|
4
|
+
const exactSequenceParser = async (parserContext) => {
|
|
5
|
+
const length = parserContext.length(sequence);
|
|
6
|
+
for (let index = 0; index < length; index++) {
|
|
7
|
+
const element = await parserContext.read(0);
|
|
8
|
+
const expectedElement = parserContext.at(sequence, index);
|
|
9
|
+
parserContext.invariant(element === expectedElement, 'Expected "%s", got "%s", at index %s', expectedElement, element, index);
|
|
10
|
+
}
|
|
11
|
+
return sequence;
|
|
12
|
+
};
|
|
13
|
+
setParserName(exactSequenceParser, inspect(sequence));
|
|
14
|
+
return exactSequenceParser;
|
|
15
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
2
|
+
export const createFixedLengthParser = (length) => {
|
|
3
|
+
parserImplementationInvariant(length > 0, 'Length must be positive.');
|
|
4
|
+
return async (parserContext) => {
|
|
5
|
+
const elements = [];
|
|
6
|
+
for (let i = 0; i < length; i++) {
|
|
7
|
+
const element = await parserContext.read(0);
|
|
8
|
+
elements.push(element);
|
|
9
|
+
}
|
|
10
|
+
return parserContext.from(elements);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
2
|
+
export const createFixedLengthSequenceParser = (length) => {
|
|
3
|
+
parserImplementationInvariant(length >= 0, 'Length must be non-negative got %s.', length);
|
|
4
|
+
return async (parserContext) => {
|
|
5
|
+
const elements = [];
|
|
6
|
+
for (let i = 0; i < length; i++) {
|
|
7
|
+
const element = await parserContext.read(0);
|
|
8
|
+
elements.push(element);
|
|
9
|
+
}
|
|
10
|
+
return parserContext.from(elements);
|
|
11
|
+
};
|
|
12
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { setParserName } from './parser.js';
|
|
2
|
+
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
3
|
+
export const createFixedLengthSequenceParser = (lengthInput) => {
|
|
4
|
+
const length = BigInt(lengthInput);
|
|
5
|
+
parserImplementationInvariant(length >= 0n, 'Length must be non-negative got %s.', length);
|
|
6
|
+
const fixedLengthSequenceParser = async (parserContext) => {
|
|
7
|
+
const elements = [];
|
|
8
|
+
for (let i = 0n; i < length; i++) {
|
|
9
|
+
const element = await parserContext.read(0);
|
|
10
|
+
elements.push(element);
|
|
11
|
+
}
|
|
12
|
+
return parserContext.from(elements);
|
|
13
|
+
};
|
|
14
|
+
setParserName(fixedLengthSequenceParser, `.{${length}}`);
|
|
15
|
+
return fixedLengthSequenceParser;
|
|
16
|
+
};
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export { Parser, runParser, setParserName, getParserName, } from './parser.js';
|
|
2
|
+
export { ParserContext, } from './parserContext.js';
|
|
3
|
+
export { createTupleParser, } from './tupleParser.js';
|
|
4
|
+
export { createExactSequenceParser, } from './exactSequenceParser.js';
|
|
5
|
+
export { createFixedLengthSequenceParser, } from './fixedLengthSequenceParser.js';
|
|
6
|
+
export { createArrayParser, } from './arrayParser.js';
|
|
7
|
+
export { createOptionalParser, } from './optionalParser.js';
|
|
8
|
+
export { createUnionParser, } from './unionParser.js';
|
|
9
|
+
export { createDisjunctionParser, } from './disjunctionParser.js';
|
|
10
|
+
export { createParserAccessorParser, } from './parserAccessorParser.js';
|
|
11
|
+
export { createElementParser, } from './elementParser.js';
|
|
12
|
+
export { createTerminatedArrayParser, } from './terminatedArrayParser.js';
|
|
13
|
+
export { createSliceBoundedParser, } from './sliceBoundedParser.js';
|
|
14
|
+
export { createExactElementParser, } from './exactElementParser.js';
|
|
15
|
+
export { createSkipParser, } from './skipParser.js';
|
|
16
|
+
export { createEndOfInputParser, } from './endOfInputParser.js';
|
|
17
|
+
export { createListParser, } from './listParser.js';
|
|
18
|
+
export { createDebugLogParser, } from './debugLogParser.js';
|
package/build/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { runParser, setParserName, getParserName, } from './parser.js';
|
|
2
|
+
export { createTupleParser, } from './tupleParser.js';
|
|
3
|
+
export { createExactSequenceParser, } from './exactSequenceParser.js';
|
|
4
|
+
export { createFixedLengthSequenceParser, } from './fixedLengthSequenceParser.js';
|
|
5
|
+
export { createArrayParser, } from './arrayParser.js';
|
|
6
|
+
export { createOptionalParser, } from './optionalParser.js';
|
|
7
|
+
export { createUnionParser, } from './unionParser.js';
|
|
8
|
+
export { createDisjunctionParser, } from './disjunctionParser.js';
|
|
9
|
+
export { createParserAccessorParser, } from './parserAccessorParser.js';
|
|
10
|
+
export { createElementParser, } from './elementParser.js';
|
|
11
|
+
export { createTerminatedArrayParser, } from './terminatedArrayParser.js';
|
|
12
|
+
export { createSliceBoundedParser, } from './sliceBoundedParser.js';
|
|
13
|
+
export { createExactElementParser, } from './exactElementParser.js';
|
|
14
|
+
export { createSkipParser, } from './skipParser.js';
|
|
15
|
+
export { createEndOfInputParser, } from './endOfInputParser.js';
|
|
16
|
+
export { createListParser, } from './listParser.js';
|
|
17
|
+
export { createDebugLogParser, } from './debugLogParser.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type InputCompanion } from './inputCompanion.js';
|
|
2
|
+
export interface SequenceBuffer<Sequence, InputElement> {
|
|
3
|
+
push(sequence: Sequence): void;
|
|
4
|
+
peek(offset: number): InputElement | undefined;
|
|
5
|
+
skip(offset: number): void;
|
|
6
|
+
}
|
|
7
|
+
export declare class SequenceBufferImplementation<Sequence, InputElement> implements SequenceBuffer<Sequence, InputElement> {
|
|
8
|
+
private readonly _inputCompanion;
|
|
9
|
+
private readonly _sequences;
|
|
10
|
+
private _indexInFirstSequence;
|
|
11
|
+
constructor(_inputCompanion: InputCompanion<Sequence, InputElement>);
|
|
12
|
+
push(sequence: Sequence): void;
|
|
13
|
+
peek(offset: number): InputElement | undefined;
|
|
14
|
+
skip(offset: number): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import invariant from 'invariant';
|
|
2
|
+
export class SequenceBufferImplementation {
|
|
3
|
+
_inputCompanion;
|
|
4
|
+
_sequences = [];
|
|
5
|
+
_indexInFirstSequence = 0;
|
|
6
|
+
constructor(_inputCompanion) {
|
|
7
|
+
this._inputCompanion = _inputCompanion;
|
|
8
|
+
}
|
|
9
|
+
push(sequence) {
|
|
10
|
+
this._sequences.push(sequence);
|
|
11
|
+
while (this._sequences.length > 0) {
|
|
12
|
+
const firstSequence = this._sequences[0];
|
|
13
|
+
const firstSequenceLength = this._inputCompanion.length(firstSequence);
|
|
14
|
+
if (firstSequenceLength === 0) {
|
|
15
|
+
this._sequences.shift();
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
if (this._indexInFirstSequence < firstSequenceLength) {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
this._sequences.shift();
|
|
22
|
+
this._indexInFirstSequence -= firstSequenceLength;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
peek(offset) {
|
|
26
|
+
invariant(offset >= 0, 'Offset must be non-negative.');
|
|
27
|
+
let index = this._indexInFirstSequence + offset;
|
|
28
|
+
for (const sequence of this._sequences) {
|
|
29
|
+
const sequenceLength = this._inputCompanion.length(sequence);
|
|
30
|
+
if (index < sequenceLength) {
|
|
31
|
+
return this._inputCompanion.at(sequence, index);
|
|
32
|
+
}
|
|
33
|
+
index -= sequenceLength;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
skip(offset) {
|
|
38
|
+
this._indexInFirstSequence += offset;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { SequenceBufferImplementation } from './sequenceBuffer.js';
|
|
3
|
+
import { stringInputCompanion } from './inputCompanion.js';
|
|
4
|
+
test('sequenceBuffer', t => {
|
|
5
|
+
const sequenceBuffer = new SequenceBufferImplementation(stringInputCompanion);
|
|
6
|
+
t.is(sequenceBuffer.peek(0), undefined);
|
|
7
|
+
sequenceBuffer.push('');
|
|
8
|
+
t.is(sequenceBuffer.peek(0), undefined);
|
|
9
|
+
sequenceBuffer.push('abc');
|
|
10
|
+
t.is(sequenceBuffer.peek(0), 'a');
|
|
11
|
+
t.is(sequenceBuffer.peek(1), 'b');
|
|
12
|
+
t.is(sequenceBuffer.peek(2), 'c');
|
|
13
|
+
t.is(sequenceBuffer.peek(3), undefined);
|
|
14
|
+
sequenceBuffer.push('def');
|
|
15
|
+
t.is(sequenceBuffer.peek(0), 'a');
|
|
16
|
+
t.is(sequenceBuffer.peek(3), 'd');
|
|
17
|
+
t.is(sequenceBuffer.peek(6), undefined);
|
|
18
|
+
sequenceBuffer.push('');
|
|
19
|
+
t.is(sequenceBuffer.peek(0), 'a');
|
|
20
|
+
t.is(sequenceBuffer.peek(3), 'd');
|
|
21
|
+
t.is(sequenceBuffer.peek(6), undefined);
|
|
22
|
+
sequenceBuffer.skip(1);
|
|
23
|
+
t.is(sequenceBuffer.peek(0), 'b');
|
|
24
|
+
t.is(sequenceBuffer.peek(3), 'e');
|
|
25
|
+
t.is(sequenceBuffer.peek(5), undefined);
|
|
26
|
+
sequenceBuffer.skip(3);
|
|
27
|
+
t.is(sequenceBuffer.peek(0), 'e');
|
|
28
|
+
t.is(sequenceBuffer.peek(2), undefined);
|
|
29
|
+
sequenceBuffer.push('gh');
|
|
30
|
+
t.is(sequenceBuffer.peek(0), 'e');
|
|
31
|
+
t.is(sequenceBuffer.peek(2), 'g');
|
|
32
|
+
t.is(sequenceBuffer.peek(3), 'h');
|
|
33
|
+
t.is(sequenceBuffer.peek(4), undefined);
|
|
34
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export type InputCompanion<Sequence, Element> = {
|
|
2
|
+
is(value: unknown): value is Sequence;
|
|
3
|
+
from(elements: Element[]): Sequence;
|
|
4
|
+
length(sequence: Sequence): number;
|
|
5
|
+
at(sequence: Sequence, index: number): Element | undefined;
|
|
6
|
+
};
|
|
7
|
+
export declare const stringInputCompanion: {
|
|
8
|
+
is(value: unknown): value is string;
|
|
9
|
+
from(elements: string[]): string;
|
|
10
|
+
length(sequence: string): number;
|
|
11
|
+
at(sequence: string, index: number): string | undefined;
|
|
12
|
+
};
|
|
13
|
+
export declare const uint8ArrayInputCompanion: {
|
|
14
|
+
is(value: unknown): value is Uint8Array;
|
|
15
|
+
from(elements: number[]): Uint8Array;
|
|
16
|
+
length(sequence: Uint8Array): number;
|
|
17
|
+
at(sequence: Uint8Array, index: number): number | undefined;
|
|
18
|
+
};
|