@futpib/parser 1.0.3 → 1.0.6
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/.claude/settings.local.json +24 -0
- package/.github/workflows/main.yml +1 -0
- package/build/androidPackageParser.js +30 -32
- package/build/arbitraryDalvikBytecode.d.ts +3 -3
- package/build/arbitraryDalvikBytecode.js +33 -27
- package/build/arbitraryDalvikExecutable.js +55 -17
- package/build/arbitraryJava.d.ts +31 -0
- package/build/arbitraryJava.js +532 -0
- package/build/arbitraryJavaScript.d.ts +3 -0
- package/build/arbitraryJavaScript.js +263 -0
- package/build/arbitraryJavascript.d.ts +3 -0
- package/build/arbitraryJavascript.js +263 -0
- package/build/arbitraryZig.d.ts +3 -0
- package/build/arbitraryZig.js +240 -0
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arrayParser.js +72 -13
- package/build/backsmali.d.ts +4 -3
- package/build/backsmali.js +26 -6
- package/build/bash.d.ts +89 -0
- package/build/bash.js +1 -0
- package/build/bashParser.d.ts +6 -0
- package/build/bashParser.js +335 -0
- package/build/bashParser.test.d.ts +1 -0
- package/build/bashParser.test.js +343 -0
- package/build/bashParserEdgeCases.test.d.ts +1 -0
- package/build/bashParserEdgeCases.test.js +117 -0
- package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
- package/build/dalvikBytecodeParser/addressConversion.js +334 -0
- package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
- package/build/dalvikBytecodeParser/formatParsers.js +13 -14
- package/build/dalvikBytecodeParser.d.ts +60 -31
- package/build/dalvikBytecodeParser.js +92 -35
- package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
- package/build/dalvikBytecodeParser.test-d.js +268 -0
- package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
- package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
- package/build/dalvikBytecodeUnparser.d.ts +2 -2
- package/build/dalvikBytecodeUnparser.js +23 -23
- package/build/dalvikBytecodeUnparser.test.js +7 -7
- package/build/dalvikExecutable.d.ts +3 -3
- package/build/dalvikExecutable.test-d.d.ts +1 -0
- package/build/dalvikExecutable.test-d.js +59 -0
- package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
- package/build/dalvikExecutableParser/typedNumbers.js +3 -0
- package/build/dalvikExecutableParser.d.ts +2 -1
- package/build/dalvikExecutableParser.js +96 -77
- package/build/dalvikExecutableParser.test.js +24 -3
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
- package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
- package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
- package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
- package/build/dalvikExecutableUnparser.d.ts +2 -2
- package/build/dalvikExecutableUnparser.test.js +2 -1
- package/build/disjunctionParser.d.ts +5 -3
- package/build/disjunctionParser.js +79 -17
- package/build/disjunctionParser.test-d.d.ts +1 -0
- package/build/disjunctionParser.test-d.js +72 -0
- package/build/elementSwitchParser.d.ts +4 -0
- package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
- package/build/elementSwitchParser.test-d.d.ts +1 -0
- package/build/elementSwitchParser.test-d.js +44 -0
- package/build/exactSequenceParser.d.ts +4 -2
- package/build/exactSequenceParser.test-d.d.ts +1 -0
- package/build/exactSequenceParser.test-d.js +36 -0
- package/build/fetchCid.js +2 -66
- package/build/index.d.ts +25 -2
- package/build/index.js +23 -1
- package/build/index.test.js +16 -1
- package/build/inputReader.d.ts +10 -0
- package/build/inputReader.js +36 -0
- package/build/java.d.ts +502 -0
- package/build/java.js +2 -0
- package/build/javaKeyStoreParser.js +14 -17
- package/build/javaParser.d.ts +51 -0
- package/build/javaParser.js +1538 -0
- package/build/javaParser.test.d.ts +1 -0
- package/build/javaParser.test.js +1287 -0
- package/build/javaScript.d.ts +35 -0
- package/build/javaScript.js +1 -0
- package/build/javaScriptParser.d.ts +9 -0
- package/build/javaScriptParser.js +34 -0
- package/build/javaScriptUnparser.d.ts +3 -0
- package/build/javaScriptUnparser.js +4 -0
- package/build/javaScriptUnparser.test.d.ts +1 -0
- package/build/javaScriptUnparser.test.js +24 -0
- package/build/javaUnparser.d.ts +2 -0
- package/build/javaUnparser.js +519 -0
- package/build/javaUnparser.test.d.ts +1 -0
- package/build/javaUnparser.test.js +24 -0
- package/build/javascript.d.ts +35 -0
- package/build/javascript.js +1 -0
- package/build/javascriptParser.d.ts +9 -0
- package/build/javascriptParser.js +34 -0
- package/build/javascriptUnparser.d.ts +3 -0
- package/build/javascriptUnparser.js +4 -0
- package/build/javascriptUnparser.test.d.ts +1 -0
- package/build/javascriptUnparser.test.js +24 -0
- package/build/jsonParser.js +2 -12
- package/build/lazyMessageError.d.ts +3 -0
- package/build/lookaheadParser.js +60 -3
- package/build/negativeLookaheadParser.js +70 -11
- package/build/nonEmptyArrayParser.js +72 -13
- package/build/objectParser.d.ts +12 -0
- package/build/objectParser.js +31 -0
- package/build/objectParser.test-d.d.ts +1 -0
- package/build/objectParser.test-d.js +112 -0
- package/build/objectParser.test.d.ts +1 -0
- package/build/objectParser.test.js +55 -0
- package/build/optionalParser.js +69 -10
- package/build/parser.d.ts +4 -0
- package/build/parser.js +3 -1
- package/build/parser.test.js +114 -1
- package/build/parserConsumedSequenceParser.js +66 -7
- package/build/parserContext.d.ts +6 -0
- package/build/parserContext.js +20 -11
- package/build/parserError.d.ts +119 -27
- package/build/parserError.js +16 -8
- package/build/regexpParser.d.ts +2 -0
- package/build/regexpParser.js +101 -0
- package/build/regexpParser.test.d.ts +1 -0
- package/build/regexpParser.test.js +114 -0
- package/build/regularExpression.d.ts +63 -0
- package/build/regularExpression.js +1 -0
- package/build/regularExpressionParser.d.ts +3 -0
- package/build/regularExpressionParser.js +600 -0
- package/build/regularExpressionParser.test.d.ts +1 -0
- package/build/regularExpressionParser.test.js +89 -0
- package/build/separatedArrayParser.js +73 -14
- package/build/separatedNonEmptyArrayParser.js +73 -14
- package/build/sliceBoundedParser.js +62 -5
- package/build/smaliParser.d.ts +7 -7
- package/build/smaliParser.js +185 -268
- package/build/smaliParser.test.js +58 -0
- package/build/stringEscapes.d.ts +5 -0
- package/build/stringEscapes.js +244 -0
- package/build/symbolicExpression.d.ts +29 -0
- package/build/symbolicExpression.js +1 -0
- package/build/symbolicExpressionParser.d.ts +4 -0
- package/build/symbolicExpressionParser.js +123 -0
- package/build/symbolicExpressionParser.test.d.ts +1 -0
- package/build/symbolicExpressionParser.test.js +289 -0
- package/build/terminatedArrayParser.js +113 -38
- package/build/terminatedArrayParser.test.js +4 -2
- package/build/tupleParser.d.ts +7 -15
- package/build/tupleParser.js +1 -0
- package/build/unionParser.d.ts +5 -3
- package/build/unionParser.js +7 -2
- package/build/unionParser.test-d.d.ts +1 -0
- package/build/unionParser.test-d.js +72 -0
- package/build/unionParser.test.js +10 -11
- package/build/zig.d.ts +280 -0
- package/build/zig.js +2 -0
- package/build/zigParser.d.ts +3 -0
- package/build/zigParser.js +1119 -0
- package/build/zigParser.test.d.ts +1 -0
- package/build/zigParser.test.js +1590 -0
- package/build/zigUnparser.d.ts +2 -0
- package/build/zigUnparser.js +460 -0
- package/build/zigUnparser.test.d.ts +1 -0
- package/build/zigUnparser.test.js +24 -0
- package/build/zipParser.js +19 -32
- package/build/zipUnparser.js +19 -7
- package/build/zipUnparser.test.js +1 -1
- package/node_modules-@types/s-expression/index.d.ts +5 -0
- package/package.json +25 -6
- package/src/androidPackageParser.ts +33 -60
- package/src/arbitraryDalvikBytecode.ts +39 -31
- package/src/arbitraryDalvikExecutable.ts +65 -20
- package/src/arbitraryJava.ts +804 -0
- package/src/arbitraryJavaScript.ts +410 -0
- package/src/arbitraryZig.ts +380 -0
- package/src/arrayParser.ts +1 -3
- package/src/backsmali.ts +35 -4
- package/src/bash.ts +127 -0
- package/src/bashParser.test.ts +590 -0
- package/src/bashParser.ts +498 -0
- package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
- package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
- package/src/dalvikBytecodeParser.test-d.ts +310 -0
- package/src/dalvikBytecodeParser.ts +194 -69
- package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
- package/src/dalvikBytecodeUnparser.test.ts +7 -7
- package/src/dalvikBytecodeUnparser.ts +31 -30
- package/src/dalvikExecutable.test-d.ts +132 -0
- package/src/dalvikExecutable.ts +3 -3
- package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
- package/src/dalvikExecutableParser.test.ts +37 -3
- package/src/dalvikExecutableParser.test.ts.md +163 -2
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +121 -139
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
- package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
- package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
- package/src/dalvikExecutableUnparser.test.ts +3 -2
- package/src/dalvikExecutableUnparser.ts +4 -4
- package/src/disjunctionParser.test-d.ts +105 -0
- package/src/disjunctionParser.ts +18 -15
- package/src/elementSwitchParser.test-d.ts +74 -0
- package/src/elementSwitchParser.ts +51 -0
- package/src/exactSequenceParser.test-d.ts +43 -0
- package/src/exactSequenceParser.ts +13 -8
- package/src/fetchCid.ts +2 -76
- package/src/index.test.ts +22 -1
- package/src/index.ts +119 -2
- package/src/inputReader.ts +53 -0
- package/src/java.ts +708 -0
- package/src/javaKeyStoreParser.ts +18 -32
- package/src/javaParser.test.ts +1592 -0
- package/src/javaParser.ts +2640 -0
- package/src/javaScript.ts +36 -0
- package/src/javaScriptParser.ts +57 -0
- package/src/javaScriptUnparser.test.ts +37 -0
- package/src/javaScriptUnparser.ts +7 -0
- package/src/javaUnparser.test.ts +37 -0
- package/src/javaUnparser.ts +640 -0
- package/src/jsonParser.ts +6 -27
- package/src/lookaheadParser.ts +2 -6
- package/src/negativeLookaheadParser.ts +1 -3
- package/src/nonEmptyArrayParser.ts +1 -3
- package/src/objectParser.test-d.ts +152 -0
- package/src/objectParser.test.ts +71 -0
- package/src/objectParser.ts +69 -0
- package/src/optionalParser.ts +1 -3
- package/src/parser.test.ts +151 -4
- package/src/parser.ts +11 -1
- package/src/parserConsumedSequenceParser.ts +2 -4
- package/src/parserContext.ts +26 -11
- package/src/parserError.ts +17 -3
- package/src/regexpParser.test.ts +264 -0
- package/src/regexpParser.ts +126 -0
- package/src/regularExpression.ts +24 -0
- package/src/regularExpressionParser.test.ts +102 -0
- package/src/regularExpressionParser.ts +920 -0
- package/src/separatedArrayParser.ts +1 -3
- package/src/separatedNonEmptyArrayParser.ts +1 -3
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +15 -19
- package/src/smaliParser.test.ts +64 -0
- package/src/smaliParser.test.ts.md +12 -12
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +246 -534
- package/src/stringEscapes.ts +253 -0
- package/src/symbolicExpression.ts +17 -0
- package/src/symbolicExpressionParser.test.ts +466 -0
- package/src/symbolicExpressionParser.ts +190 -0
- package/src/terminatedArrayParser.test.ts +9 -6
- package/src/terminatedArrayParser.ts +25 -29
- package/src/tupleParser.ts +21 -18
- package/src/unionParser.test-d.ts +105 -0
- package/src/unionParser.test.ts +18 -17
- package/src/unionParser.ts +28 -16
- package/src/zig.ts +411 -0
- package/src/zigParser.test.ts +1693 -0
- package/src/zigParser.ts +1745 -0
- package/src/zigUnparser.test.ts +37 -0
- package/src/zigUnparser.ts +615 -0
- package/src/zipParser.ts +20 -56
- package/src/zipUnparser.test.ts +1 -1
- package/src/zipUnparser.ts +22 -7
- package/tsconfig.json +2 -2
- package/build/exactElementSwitchParser.d.ts +0 -3
- package/src/exactElementSwitchParser.ts +0 -41
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import test from 'ava';
|
|
2
|
+
import { testProp, fc } from '@fast-check/ava';
|
|
3
|
+
import sExpression from 's-expression';
|
|
4
|
+
import { runParser } from './parser.js';
|
|
5
|
+
const seed = process.env.SEED ? Number(process.env.SEED) : undefined;
|
|
6
|
+
import { stringParserInputCompanion } from './parserInputCompanion.js';
|
|
7
|
+
import { symbolicExpressionParser, symbolicExpressionDocumentParser, } from './symbolicExpressionParser.js';
|
|
8
|
+
// Convert s-expression package output to our discriminated union format
|
|
9
|
+
function convertToDiscriminatedUnion(value) {
|
|
10
|
+
if (Array.isArray(value)) {
|
|
11
|
+
// Handle quote forms: ['quote', x], ['quasiquote', x], etc.
|
|
12
|
+
if (value.length === 2 && typeof value[0] === 'string') {
|
|
13
|
+
const [type, inner] = value;
|
|
14
|
+
if (type === 'quote') {
|
|
15
|
+
return { type: 'quote', value: convertToDiscriminatedUnion(inner) };
|
|
16
|
+
}
|
|
17
|
+
if (type === 'quasiquote') {
|
|
18
|
+
return { type: 'quasiquote', value: convertToDiscriminatedUnion(inner) };
|
|
19
|
+
}
|
|
20
|
+
if (type === 'unquote') {
|
|
21
|
+
return { type: 'unquote', value: convertToDiscriminatedUnion(inner) };
|
|
22
|
+
}
|
|
23
|
+
if (type === 'unquote-splicing') {
|
|
24
|
+
return { type: 'unquote-splicing', value: convertToDiscriminatedUnion(inner) };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return { type: 'list', value: value.map(convertToDiscriminatedUnion) };
|
|
28
|
+
}
|
|
29
|
+
if (value instanceof String) {
|
|
30
|
+
return { type: 'string', value: value.valueOf() };
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === 'string') {
|
|
33
|
+
return { type: 'atom', value };
|
|
34
|
+
}
|
|
35
|
+
throw new Error(`Unknown value type: ${typeof value}`);
|
|
36
|
+
}
|
|
37
|
+
// Basic atom parsing
|
|
38
|
+
test('atom - simple', async (t) => {
|
|
39
|
+
const result = await runParser(symbolicExpressionParser, 'a', stringParserInputCompanion);
|
|
40
|
+
t.deepEqual(result, { type: 'atom', value: 'a' });
|
|
41
|
+
});
|
|
42
|
+
test('atom - with numbers', async (t) => {
|
|
43
|
+
const result = await runParser(symbolicExpressionParser, 'abc123', stringParserInputCompanion);
|
|
44
|
+
t.deepEqual(result, { type: 'atom', value: 'abc123' });
|
|
45
|
+
});
|
|
46
|
+
// List parsing
|
|
47
|
+
test('list - empty', async (t) => {
|
|
48
|
+
const result = await runParser(symbolicExpressionParser, '()', stringParserInputCompanion);
|
|
49
|
+
t.deepEqual(result, { type: 'list', value: [] });
|
|
50
|
+
});
|
|
51
|
+
test('list - single element', async (t) => {
|
|
52
|
+
const result = await runParser(symbolicExpressionParser, '(a)', stringParserInputCompanion);
|
|
53
|
+
t.deepEqual(result, {
|
|
54
|
+
type: 'list',
|
|
55
|
+
value: [{ type: 'atom', value: 'a' }],
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
test('list - multiple elements', async (t) => {
|
|
59
|
+
const result = await runParser(symbolicExpressionParser, '(a b c)', stringParserInputCompanion);
|
|
60
|
+
t.deepEqual(result, {
|
|
61
|
+
type: 'list',
|
|
62
|
+
value: [
|
|
63
|
+
{ type: 'atom', value: 'a' },
|
|
64
|
+
{ type: 'atom', value: 'b' },
|
|
65
|
+
{ type: 'atom', value: 'c' },
|
|
66
|
+
],
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
test('list - nested', async (t) => {
|
|
70
|
+
const result = await runParser(symbolicExpressionParser, '((a b c)(()()))', stringParserInputCompanion);
|
|
71
|
+
t.deepEqual(result, {
|
|
72
|
+
type: 'list',
|
|
73
|
+
value: [
|
|
74
|
+
{
|
|
75
|
+
type: 'list',
|
|
76
|
+
value: [
|
|
77
|
+
{ type: 'atom', value: 'a' },
|
|
78
|
+
{ type: 'atom', value: 'b' },
|
|
79
|
+
{ type: 'atom', value: 'c' },
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'list',
|
|
84
|
+
value: [
|
|
85
|
+
{ type: 'list', value: [] },
|
|
86
|
+
{ type: 'list', value: [] },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
// String parsing
|
|
93
|
+
test('string - simple', async (t) => {
|
|
94
|
+
const result = await runParser(symbolicExpressionParser, '"hello"', stringParserInputCompanion);
|
|
95
|
+
t.deepEqual(result, { type: 'string', value: 'hello' });
|
|
96
|
+
});
|
|
97
|
+
test('string - with escape sequences', async (t) => {
|
|
98
|
+
const result = await runParser(symbolicExpressionParser, '"hello\\nworld"', stringParserInputCompanion);
|
|
99
|
+
t.deepEqual(result, { type: 'string', value: 'hello\nworld' });
|
|
100
|
+
});
|
|
101
|
+
test('string - in list', async (t) => {
|
|
102
|
+
const result = await runParser(symbolicExpressionParser, '(a "hello")', stringParserInputCompanion);
|
|
103
|
+
t.deepEqual(result, {
|
|
104
|
+
type: 'list',
|
|
105
|
+
value: [
|
|
106
|
+
{ type: 'atom', value: 'a' },
|
|
107
|
+
{ type: 'string', value: 'hello' },
|
|
108
|
+
],
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
// Quote parsing
|
|
112
|
+
test('quote - atom', async (t) => {
|
|
113
|
+
const result = await runParser(symbolicExpressionParser, "'a", stringParserInputCompanion);
|
|
114
|
+
t.deepEqual(result, {
|
|
115
|
+
type: 'quote',
|
|
116
|
+
value: { type: 'atom', value: 'a' },
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
test('quote - empty list', async (t) => {
|
|
120
|
+
const result = await runParser(symbolicExpressionParser, "'()", stringParserInputCompanion);
|
|
121
|
+
t.deepEqual(result, {
|
|
122
|
+
type: 'quote',
|
|
123
|
+
value: { type: 'list', value: [] },
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
test('quote - list', async (t) => {
|
|
127
|
+
const result = await runParser(symbolicExpressionParser, "'(a b c)", stringParserInputCompanion);
|
|
128
|
+
t.deepEqual(result, {
|
|
129
|
+
type: 'quote',
|
|
130
|
+
value: {
|
|
131
|
+
type: 'list',
|
|
132
|
+
value: [
|
|
133
|
+
{ type: 'atom', value: 'a' },
|
|
134
|
+
{ type: 'atom', value: 'b' },
|
|
135
|
+
{ type: 'atom', value: 'c' },
|
|
136
|
+
],
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
test('quote - nested', async (t) => {
|
|
141
|
+
const result = await runParser(symbolicExpressionParser, "''a", stringParserInputCompanion);
|
|
142
|
+
t.deepEqual(result, {
|
|
143
|
+
type: 'quote',
|
|
144
|
+
value: {
|
|
145
|
+
type: 'quote',
|
|
146
|
+
value: { type: 'atom', value: 'a' },
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
// Quasiquote parsing
|
|
151
|
+
test('quasiquote - atom', async (t) => {
|
|
152
|
+
const result = await runParser(symbolicExpressionParser, '`a', stringParserInputCompanion);
|
|
153
|
+
t.deepEqual(result, {
|
|
154
|
+
type: 'quasiquote',
|
|
155
|
+
value: { type: 'atom', value: 'a' },
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
test('quasiquote - list', async (t) => {
|
|
159
|
+
const result = await runParser(symbolicExpressionParser, '`(a b c)', stringParserInputCompanion);
|
|
160
|
+
t.deepEqual(result, {
|
|
161
|
+
type: 'quasiquote',
|
|
162
|
+
value: {
|
|
163
|
+
type: 'list',
|
|
164
|
+
value: [
|
|
165
|
+
{ type: 'atom', value: 'a' },
|
|
166
|
+
{ type: 'atom', value: 'b' },
|
|
167
|
+
{ type: 'atom', value: 'c' },
|
|
168
|
+
],
|
|
169
|
+
},
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
// Unquote parsing
|
|
173
|
+
test('unquote - atom', async (t) => {
|
|
174
|
+
const result = await runParser(symbolicExpressionParser, ',a', stringParserInputCompanion);
|
|
175
|
+
t.deepEqual(result, {
|
|
176
|
+
type: 'unquote',
|
|
177
|
+
value: { type: 'atom', value: 'a' },
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
test('unquote - list', async (t) => {
|
|
181
|
+
const result = await runParser(symbolicExpressionParser, ',(a b c)', stringParserInputCompanion);
|
|
182
|
+
t.deepEqual(result, {
|
|
183
|
+
type: 'unquote',
|
|
184
|
+
value: {
|
|
185
|
+
type: 'list',
|
|
186
|
+
value: [
|
|
187
|
+
{ type: 'atom', value: 'a' },
|
|
188
|
+
{ type: 'atom', value: 'b' },
|
|
189
|
+
{ type: 'atom', value: 'c' },
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
// Unquote-splicing parsing
|
|
195
|
+
test('unquote-splicing - atom', async (t) => {
|
|
196
|
+
const result = await runParser(symbolicExpressionParser, ',@a', stringParserInputCompanion);
|
|
197
|
+
t.deepEqual(result, {
|
|
198
|
+
type: 'unquote-splicing',
|
|
199
|
+
value: { type: 'atom', value: 'a' },
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
test('unquote-splicing - list', async (t) => {
|
|
203
|
+
const result = await runParser(symbolicExpressionParser, ',@(a b c)', stringParserInputCompanion);
|
|
204
|
+
t.deepEqual(result, {
|
|
205
|
+
type: 'unquote-splicing',
|
|
206
|
+
value: {
|
|
207
|
+
type: 'list',
|
|
208
|
+
value: [
|
|
209
|
+
{ type: 'atom', value: 'a' },
|
|
210
|
+
{ type: 'atom', value: 'b' },
|
|
211
|
+
{ type: 'atom', value: 'c' },
|
|
212
|
+
],
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
// Complex expressions
|
|
217
|
+
test('complex - quote inside list', async (t) => {
|
|
218
|
+
const result = await runParser(symbolicExpressionParser, "(a '(a b c))", stringParserInputCompanion);
|
|
219
|
+
t.deepEqual(result, {
|
|
220
|
+
type: 'list',
|
|
221
|
+
value: [
|
|
222
|
+
{ type: 'atom', value: 'a' },
|
|
223
|
+
{
|
|
224
|
+
type: 'quote',
|
|
225
|
+
value: {
|
|
226
|
+
type: 'list',
|
|
227
|
+
value: [
|
|
228
|
+
{ type: 'atom', value: 'a' },
|
|
229
|
+
{ type: 'atom', value: 'b' },
|
|
230
|
+
{ type: 'atom', value: 'c' },
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
},
|
|
234
|
+
],
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
test('complex - multiple quotes in list', async (t) => {
|
|
238
|
+
const result = await runParser(symbolicExpressionParser, "((a 'b 'c))", stringParserInputCompanion);
|
|
239
|
+
t.deepEqual(result, {
|
|
240
|
+
type: 'list',
|
|
241
|
+
value: [
|
|
242
|
+
{
|
|
243
|
+
type: 'list',
|
|
244
|
+
value: [
|
|
245
|
+
{ type: 'atom', value: 'a' },
|
|
246
|
+
{ type: 'quote', value: { type: 'atom', value: 'b' } },
|
|
247
|
+
{ type: 'quote', value: { type: 'atom', value: 'c' } },
|
|
248
|
+
],
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
// Document parser with whitespace
|
|
254
|
+
test('document - with leading whitespace', async (t) => {
|
|
255
|
+
const result = await runParser(symbolicExpressionDocumentParser, ' a', stringParserInputCompanion);
|
|
256
|
+
t.deepEqual(result, { type: 'atom', value: 'a' });
|
|
257
|
+
});
|
|
258
|
+
test('document - with trailing whitespace', async (t) => {
|
|
259
|
+
const result = await runParser(symbolicExpressionDocumentParser, 'a ', stringParserInputCompanion);
|
|
260
|
+
t.deepEqual(result, { type: 'atom', value: 'a' });
|
|
261
|
+
});
|
|
262
|
+
// Arbitrary symbolic expression generator for property-based testing
|
|
263
|
+
const arbitraryAtomChar = fc.string({ minLength: 1, maxLength: 10 }).filter(s => s.length > 0 && !/[\s()"'`,;]/.test(s));
|
|
264
|
+
const arbitraryStringContent = fc.string({ maxLength: 20 }).map(s =>
|
|
265
|
+
// Escape special characters for string literals
|
|
266
|
+
s.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/\t/g, '\\t'));
|
|
267
|
+
const arbitrarySymbolicExpression = fc.letrec(tie => ({
|
|
268
|
+
atom: arbitraryAtomChar,
|
|
269
|
+
string: arbitraryStringContent.map(s => `"${s}"`),
|
|
270
|
+
list: fc.array(tie('expression'), { maxLength: 5 }).map(exprs => `(${exprs.join(' ')})`),
|
|
271
|
+
quote: tie('expression').map(e => `'${e}`),
|
|
272
|
+
quasiquote: tie('expression').map(e => `\`${e}`),
|
|
273
|
+
unquote: tie('expression').map(e => `,${e}`),
|
|
274
|
+
unquoteSplicing: tie('expression').map(e => `,@${e}`),
|
|
275
|
+
expression: fc.oneof({ weight: 5, arbitrary: tie('atom') }, { weight: 2, arbitrary: tie('string') }, { weight: 3, arbitrary: tie('list') }, { weight: 1, arbitrary: tie('quote') }, { weight: 1, arbitrary: tie('quasiquote') }, { weight: 1, arbitrary: tie('unquote') }, { weight: 1, arbitrary: tie('unquoteSplicing') }),
|
|
276
|
+
})).expression;
|
|
277
|
+
// Property-based test comparing with s-expression package
|
|
278
|
+
testProp('matches s-expression package output', [arbitrarySymbolicExpression], async (t, input) => {
|
|
279
|
+
const reference = sExpression(input);
|
|
280
|
+
if (reference instanceof Error) {
|
|
281
|
+
// If s-expression fails, our parser should also fail
|
|
282
|
+
await t.throwsAsync(() => runParser(symbolicExpressionParser, input, stringParserInputCompanion));
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
const expected = convertToDiscriminatedUnion(reference);
|
|
286
|
+
const actual = await runParser(symbolicExpressionParser, input, stringParserInputCompanion);
|
|
287
|
+
t.deepEqual(actual, expected);
|
|
288
|
+
}
|
|
289
|
+
}, { seed });
|
|
@@ -1,3 +1,55 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
1
53
|
import { getParserName, setParserName } from './parser.js';
|
|
2
54
|
import { isParserParsingFailedError } from './parserError.js';
|
|
3
55
|
import { parserImplementationInvariant } from './parserImplementationInvariant.js';
|
|
@@ -33,43 +85,59 @@ export const createTerminatedArrayParser = (elementParser, terminatorParser) =>
|
|
|
33
85
|
const terminatedArrayParser = async (parserContext) => {
|
|
34
86
|
const elements = [];
|
|
35
87
|
while (true) {
|
|
36
|
-
const
|
|
37
|
-
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
38
|
-
});
|
|
88
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
39
89
|
try {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
});
|
|
90
|
+
const terminatorParserContext = __addDisposableResource(env_1, parserContext.lookahead({
|
|
91
|
+
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
92
|
+
}), false);
|
|
44
93
|
try {
|
|
45
|
-
await
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
94
|
+
const terminatorValue = await terminatorParser(terminatorParserContext);
|
|
95
|
+
{
|
|
96
|
+
const env_2 = { stack: [], error: void 0, hasError: false };
|
|
97
|
+
try {
|
|
98
|
+
const elementParserContext = __addDisposableResource(env_2, parserContext.lookahead({
|
|
99
|
+
debugName: getParserName(elementParser, 'anonymousElement'),
|
|
100
|
+
}), false);
|
|
101
|
+
try {
|
|
102
|
+
await elementParser(elementParserContext);
|
|
103
|
+
parserImplementationInvariant(false, [
|
|
104
|
+
'Both element and terminator parsers matched.',
|
|
105
|
+
'Element parser: %s',
|
|
106
|
+
'Terminator parser: %s',
|
|
107
|
+
], getParserName(elementParser, 'anonymousElement'), getParserName(terminatorParser, 'anonymousTerminator'));
|
|
108
|
+
}
|
|
109
|
+
catch (error) {
|
|
110
|
+
if (!(isParserParsingFailedError(error))) {
|
|
111
|
+
throw error;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e_1) {
|
|
116
|
+
env_2.error = e_1;
|
|
117
|
+
env_2.hasError = true;
|
|
118
|
+
}
|
|
119
|
+
finally {
|
|
120
|
+
__disposeResources(env_2);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
terminatorParserContext.unlookahead();
|
|
124
|
+
return [elements, terminatorValue];
|
|
51
125
|
}
|
|
52
126
|
catch (error) {
|
|
53
127
|
if (!(isParserParsingFailedError(error))) {
|
|
54
128
|
throw error;
|
|
55
129
|
}
|
|
56
130
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
terminatorParserContext.unlookahead();
|
|
61
|
-
return [elements, terminatorValue];
|
|
131
|
+
const element = await elementParser(parserContext);
|
|
132
|
+
elements.push(element);
|
|
62
133
|
}
|
|
63
|
-
catch (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
}
|
|
134
|
+
catch (e_2) {
|
|
135
|
+
env_1.error = e_2;
|
|
136
|
+
env_1.hasError = true;
|
|
67
137
|
}
|
|
68
138
|
finally {
|
|
69
|
-
|
|
139
|
+
__disposeResources(env_1);
|
|
70
140
|
}
|
|
71
|
-
const element = await elementParser(parserContext);
|
|
72
|
-
elements.push(element);
|
|
73
141
|
}
|
|
74
142
|
};
|
|
75
143
|
setParserName(terminatedArrayParser, `${getParserName(elementParser, 'anonymousElement')}*?${getParserName(terminatorParser, 'anonymousTerminator')}`);
|
|
@@ -79,24 +147,31 @@ export const createTerminatedArrayParserUnsafe = (elementParser, terminatorParse
|
|
|
79
147
|
const terminatedArrayParserUnsafe = async (parserContext) => {
|
|
80
148
|
const elements = [];
|
|
81
149
|
while (true) {
|
|
82
|
-
const
|
|
83
|
-
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
84
|
-
});
|
|
150
|
+
const env_3 = { stack: [], error: void 0, hasError: false };
|
|
85
151
|
try {
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
152
|
+
const terminatorParserContext = __addDisposableResource(env_3, parserContext.lookahead({
|
|
153
|
+
debugName: getParserName(terminatorParser, 'anonymousTerminator'),
|
|
154
|
+
}), false);
|
|
155
|
+
try {
|
|
156
|
+
const terminatorValue = await terminatorParser(terminatorParserContext);
|
|
157
|
+
terminatorParserContext.unlookahead();
|
|
158
|
+
return [elements, terminatorValue];
|
|
93
159
|
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
if (!(isParserParsingFailedError(error))) {
|
|
162
|
+
throw error;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
const element = await elementParser(parserContext);
|
|
166
|
+
elements.push(element);
|
|
167
|
+
}
|
|
168
|
+
catch (e_3) {
|
|
169
|
+
env_3.error = e_3;
|
|
170
|
+
env_3.hasError = true;
|
|
94
171
|
}
|
|
95
172
|
finally {
|
|
96
|
-
|
|
173
|
+
__disposeResources(env_3);
|
|
97
174
|
}
|
|
98
|
-
const element = await elementParser(parserContext);
|
|
99
|
-
elements.push(element);
|
|
100
175
|
}
|
|
101
176
|
};
|
|
102
177
|
setParserName(terminatedArrayParserUnsafe, `${getParserName(elementParser, 'anonymousElement')}*?${getParserName(terminatorParser, 'anonymousTerminator')}`);
|
|
@@ -13,10 +13,12 @@ import { promiseCompose } from './promiseCompose.js';
|
|
|
13
13
|
import { createUnionParser } from './unionParser.js';
|
|
14
14
|
import { createExactElementParser } from './exactElementParser.js';
|
|
15
15
|
test('terminatedArrayParser of union parsers', async (t) => {
|
|
16
|
-
const
|
|
16
|
+
const elementParser = createUnionParser([
|
|
17
17
|
createExactElementParser(1),
|
|
18
18
|
createExactElementParser(2),
|
|
19
|
-
])
|
|
19
|
+
]);
|
|
20
|
+
const terminatorParser = createExactElementParser(0);
|
|
21
|
+
const parser = createTerminatedArrayParser(elementParser, terminatorParser);
|
|
20
22
|
const input = new Uint8Array([0]);
|
|
21
23
|
const result = await runParser(parser, input, uint8ArrayParserInputCompanion);
|
|
22
24
|
t.deepEqual(result, [[], 0]);
|
package/build/tupleParser.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
|
-
import { type Parser } from './parser.js';
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export declare function createTupleParser<
|
|
7
|
-
export
|
|
8
|
-
export declare function createTupleParser<A, B, C, D, E, F, G, H, Sequence>([parserA, parserB, parserC, parserD, parserE, parserF, parserG, parserH]: [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<[A, B, C, D, E, F, G, H], Sequence>;
|
|
9
|
-
export declare 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>;
|
|
10
|
-
export declare 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>;
|
|
11
|
-
export declare 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>;
|
|
12
|
-
export declare 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>;
|
|
13
|
-
export declare 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>;
|
|
14
|
-
export declare 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>;
|
|
15
|
-
export declare 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>;
|
|
1
|
+
import { type Parser, type ParserOutput, type ParserSequence } from './parser.js';
|
|
2
|
+
type TupleParserOutput<Parsers extends readonly Parser<any, any, any>[]> = {
|
|
3
|
+
[K in keyof Parsers]: Parsers[K] extends Parser<any, any, any> ? ParserOutput<Parsers[K]> : never;
|
|
4
|
+
};
|
|
5
|
+
type InferSequenceFromParserArray<T extends readonly unknown[]> = ParserSequence<T[number]>;
|
|
6
|
+
export declare function createTupleParser<const Parsers extends readonly Parser<any, any, any>[]>(parsers: Parsers): Parser<TupleParserOutput<Parsers>, InferSequenceFromParserArray<Parsers>, any>;
|
|
7
|
+
export {};
|
package/build/tupleParser.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getParserName, setParserName } from './parser.js';
|
|
2
2
|
export function createTupleParser(parsers) {
|
|
3
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3
4
|
const tupleParser = async (parserContext) => {
|
|
4
5
|
const values = [];
|
|
5
6
|
for (const parser of parsers) {
|
package/build/unionParser.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
-
import { type Parser } from './parser.js';
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { type Parser, type ParserOutput, type ParserSequence } from './parser.js';
|
|
2
|
+
type UnionParserOutput<Parsers extends readonly unknown[]> = ParserOutput<Parsers[number]>;
|
|
3
|
+
type InferSequenceFromParserArray<T extends readonly unknown[]> = ParserSequence<T[number]>;
|
|
4
|
+
export declare function createUnionParser<const Parsers extends readonly Parser<any, any, any>[]>(childParsers: Parsers): Parser<UnionParserOutput<Parsers>, InferSequenceFromParserArray<Parsers>>;
|
|
5
|
+
export {};
|
package/build/unionParser.js
CHANGED
|
@@ -8,10 +8,12 @@ const bigintReplacer = (_key, value) => {
|
|
|
8
8
|
}
|
|
9
9
|
return value;
|
|
10
10
|
};
|
|
11
|
-
export
|
|
11
|
+
export function createUnionParser(childParsers) {
|
|
12
12
|
parserImplementationInvariant(childParsers.length > 0, 'Union parser must have at least one child parser.');
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
13
14
|
const unionParser = async (parserContext) => {
|
|
14
15
|
let runningChildParserContexts = [];
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
15
17
|
const createChildParserTask = (childParser) => {
|
|
16
18
|
const childParserContext = parserContext.lookahead({
|
|
17
19
|
debugName: getParserName(childParser, 'anonymousUnionChild'),
|
|
@@ -21,6 +23,7 @@ export const createUnionParser = (childParsers) => {
|
|
|
21
23
|
childParserContext,
|
|
22
24
|
};
|
|
23
25
|
runningChildParserContexts.push(context);
|
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
24
27
|
const getChildParserPromise = (async () => childParser(childParserContext));
|
|
25
28
|
const promise = getChildParserPromise();
|
|
26
29
|
return {
|
|
@@ -28,8 +31,10 @@ export const createUnionParser = (childParsers) => {
|
|
|
28
31
|
context,
|
|
29
32
|
};
|
|
30
33
|
};
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
35
|
const childParserResults = allSettledStream(childParsers.map(createChildParserTask));
|
|
32
36
|
const parserParsingFailedErrors = [];
|
|
37
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
38
|
const successfulParserOutputs = [];
|
|
34
39
|
const successfulTaskContexts = [];
|
|
35
40
|
let didUnlookahead = false;
|
|
@@ -80,4 +85,4 @@ export const createUnionParser = (childParsers) => {
|
|
|
80
85
|
')',
|
|
81
86
|
].join('');
|
|
82
87
|
return setParserName(unionParser, name);
|
|
83
|
-
}
|
|
88
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { expectAssignable } from 'tsd';
|
|
2
|
+
import { createUnionParser } from './unionParser.js';
|
|
3
|
+
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
4
|
+
import { createExactElementParser } from './exactElementParser.js';
|
|
5
|
+
import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js';
|
|
6
|
+
// Test: basic union of string parsers - output inferred as string
|
|
7
|
+
{
|
|
8
|
+
const parser = createUnionParser([
|
|
9
|
+
createExactElementParser('a'),
|
|
10
|
+
createExactElementParser('b'),
|
|
11
|
+
]);
|
|
12
|
+
expectAssignable(null);
|
|
13
|
+
expectAssignable(null);
|
|
14
|
+
}
|
|
15
|
+
// Test: union preserves literal types when parsers have explicit literal output types
|
|
16
|
+
{
|
|
17
|
+
const parserA = async () => 'a';
|
|
18
|
+
const parserB = async () => 'b';
|
|
19
|
+
const parser = createUnionParser([parserA, parserB]);
|
|
20
|
+
// Output should be 'a' | 'b', not string
|
|
21
|
+
expectAssignable(null);
|
|
22
|
+
expectAssignable(null);
|
|
23
|
+
}
|
|
24
|
+
// Test: union of parsers with different output types
|
|
25
|
+
{
|
|
26
|
+
const stringParser = createFixedLengthSequenceParser(3);
|
|
27
|
+
const numberParser = () => 42;
|
|
28
|
+
const parser = createUnionParser([
|
|
29
|
+
stringParser,
|
|
30
|
+
numberParser,
|
|
31
|
+
]);
|
|
32
|
+
// Output should be string | number
|
|
33
|
+
expectAssignable(null);
|
|
34
|
+
expectAssignable(null);
|
|
35
|
+
}
|
|
36
|
+
// Test: nested unions
|
|
37
|
+
{
|
|
38
|
+
const inner = createUnionParser([
|
|
39
|
+
createExactElementParser('a'),
|
|
40
|
+
createExactElementParser('b'),
|
|
41
|
+
]);
|
|
42
|
+
const parser = createUnionParser([
|
|
43
|
+
inner,
|
|
44
|
+
createExactElementParser('c'),
|
|
45
|
+
]);
|
|
46
|
+
expectAssignable(null);
|
|
47
|
+
expectAssignable(null);
|
|
48
|
+
}
|
|
49
|
+
// Test: sequence type inferred from child parsers
|
|
50
|
+
{
|
|
51
|
+
const parser = createUnionParser([
|
|
52
|
+
createExactSequenceParser('hello'),
|
|
53
|
+
createExactSequenceParser('world'),
|
|
54
|
+
]);
|
|
55
|
+
// Parser should be for string sequences, output is string (widened from literals)
|
|
56
|
+
expectAssignable(null);
|
|
57
|
+
}
|
|
58
|
+
// Test: single parser in union
|
|
59
|
+
{
|
|
60
|
+
const parser = createUnionParser([
|
|
61
|
+
createExactSequenceParser('only'),
|
|
62
|
+
]);
|
|
63
|
+
// Output is string (widened from literal 'only')
|
|
64
|
+
expectAssignable(null);
|
|
65
|
+
}
|
|
66
|
+
// Test: union of object-producing parsers
|
|
67
|
+
{
|
|
68
|
+
const parser1 = async () => ({ type: 'a', value: 1 });
|
|
69
|
+
const parser2 = async () => ({ type: 'b', name: 'test' });
|
|
70
|
+
const parser = createUnionParser([parser1, parser2]);
|
|
71
|
+
expectAssignable(null);
|
|
72
|
+
}
|