@futpib/parser 1.0.4 → 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 +6 -1
- package/build/bashParser.js +131 -90
- package/build/bashParser.test.js +162 -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 +3 -2
- package/build/index.js +2 -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.js +33 -3
- package/build/regexpParser.test.js +31 -0
- package/build/regularExpressionParser.js +35 -15
- 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 +24 -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 +8 -1
- package/src/bashParser.test.ts +258 -0
- package/src/bashParser.ts +180 -143
- 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 +7 -1
- 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 +78 -0
- package/src/regexpParser.ts +35 -3
- package/src/regularExpressionParser.ts +36 -37
- 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
package/src/parserContext.ts
CHANGED
|
@@ -36,6 +36,8 @@ export type ParserContext<Sequence, Element> = {
|
|
|
36
36
|
equals(sequenceA: Sequence, sequenceB: Sequence): boolean;
|
|
37
37
|
|
|
38
38
|
get position(): number;
|
|
39
|
+
get furthestReadPosition(): number;
|
|
40
|
+
get furthestPeekedPosition(): number;
|
|
39
41
|
peek(offset: number): Promise<Element | undefined>;
|
|
40
42
|
peekSequence(start: number, end: number): Promise<Sequence | undefined>;
|
|
41
43
|
skip(offset: number): void;
|
|
@@ -46,6 +48,7 @@ export type ParserContext<Sequence, Element> = {
|
|
|
46
48
|
lookahead(options?: LookaheadOptions): ParserContext<Sequence, Element>;
|
|
47
49
|
unlookahead(): void;
|
|
48
50
|
dispose(): void;
|
|
51
|
+
[Symbol.dispose](): void;
|
|
49
52
|
|
|
50
53
|
invariant<T>(value: T, format: ValueOrAccessor<string | string[]>, ...formatArguments: unknown[]): Exclude<T, Falsy>;
|
|
51
54
|
invariantJoin<T>(value: T, childErrors: ParserParsingFailedError[], format: ValueOrAccessor<string | string[]>, ...formatArguments: unknown[]): Exclude<T, Falsy>;
|
|
@@ -117,6 +120,14 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
117
120
|
return this._inputReader.position;
|
|
118
121
|
}
|
|
119
122
|
|
|
123
|
+
get furthestReadPosition() {
|
|
124
|
+
return this._inputReader.furthestReadPosition;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
get furthestPeekedPosition() {
|
|
128
|
+
return this._inputReader.furthestPeekedPosition;
|
|
129
|
+
}
|
|
130
|
+
|
|
120
131
|
async peek(offset: number): Promise<Element | undefined> {
|
|
121
132
|
if (
|
|
122
133
|
this._options.sliceEnd !== undefined
|
|
@@ -169,7 +180,7 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
169
180
|
const element = await this.peek(offset);
|
|
170
181
|
|
|
171
182
|
if (element === undefined) {
|
|
172
|
-
throw new this._options.errorsModule.ParserUnexpectedEndOfInputError('', this._depth, this.position);
|
|
183
|
+
throw new this._options.errorsModule.ParserUnexpectedEndOfInputError('', this._depth, this.position, this._inputReader.furthestReadPosition, this._inputReader.furthestPeekedPosition);
|
|
173
184
|
}
|
|
174
185
|
|
|
175
186
|
this.skip(offset + 1);
|
|
@@ -181,7 +192,7 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
181
192
|
const sequence = await this.peekSequence(start, end);
|
|
182
193
|
|
|
183
194
|
if (sequence === undefined) {
|
|
184
|
-
throw new this._options.errorsModule.ParserUnexpectedEndOfInputError('', this._depth, this.position);
|
|
195
|
+
throw new this._options.errorsModule.ParserUnexpectedEndOfInputError('', this._depth, this.position, this._inputReader.furthestReadPosition, this._inputReader.furthestPeekedPosition);
|
|
185
196
|
}
|
|
186
197
|
|
|
187
198
|
this.skip(end);
|
|
@@ -300,11 +311,15 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
300
311
|
this._parentParserContext = undefined;
|
|
301
312
|
}
|
|
302
313
|
|
|
314
|
+
[Symbol.dispose]() {
|
|
315
|
+
this.dispose();
|
|
316
|
+
}
|
|
317
|
+
|
|
303
318
|
invariant<T>(value: T, format: ValueOrAccessor<string | string[]>, ...formatArguments: unknown[]): Exclude<T, Falsy> {
|
|
304
319
|
const parserContext = this;
|
|
305
320
|
|
|
306
321
|
return customInvariant(function (lazyMessage: LazyMessage) {
|
|
307
|
-
return new parserContext._options.errorsModule.ParserParsingInvariantError(lazyMessage, parserContext._depth, parserContext.position);
|
|
322
|
+
return new parserContext._options.errorsModule.ParserParsingInvariantError(lazyMessage, parserContext._depth, parserContext.position, parserContext._inputReader.furthestReadPosition, parserContext._inputReader.furthestPeekedPosition);
|
|
308
323
|
}, value, format, ...formatArguments);
|
|
309
324
|
}
|
|
310
325
|
|
|
@@ -316,22 +331,22 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
316
331
|
|
|
317
332
|
if (errorJoinMode === 'none') {
|
|
318
333
|
return customInvariant(function (lazyMessage: LazyMessage) {
|
|
319
|
-
return new parserContext._options.errorsModule.ParserParsingJoinNoneError(lazyMessage, parserContext._depth, parserContext.position);
|
|
334
|
+
return new parserContext._options.errorsModule.ParserParsingJoinNoneError(lazyMessage, parserContext._depth, parserContext.position, parserContext._inputReader.furthestReadPosition, parserContext._inputReader.furthestPeekedPosition);
|
|
320
335
|
}, value, format, ...formatArguments);
|
|
321
336
|
}
|
|
322
337
|
|
|
323
338
|
if (errorJoinMode === 'furthest') {
|
|
324
339
|
return customInvariant(function (userLazyMessage: LazyMessage) {
|
|
325
|
-
let
|
|
340
|
+
let furthestChildErrorPosition = 0;
|
|
326
341
|
let furthestChildErrors: ParserParsingFailedError[] = [];
|
|
327
342
|
|
|
328
343
|
for (const childError of childErrors) {
|
|
329
|
-
if (childError.position <
|
|
344
|
+
if (childError.position < furthestChildErrorPosition) {
|
|
330
345
|
continue;
|
|
331
346
|
}
|
|
332
347
|
|
|
333
|
-
if (childError.position >
|
|
334
|
-
|
|
348
|
+
if (childError.position > furthestChildErrorPosition) {
|
|
349
|
+
furthestChildErrorPosition = childError.position;
|
|
335
350
|
furthestChildErrors = [ childError ];
|
|
336
351
|
continue;
|
|
337
352
|
}
|
|
@@ -354,7 +369,7 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
354
369
|
|
|
355
370
|
return furthestChildError.stack?.split('\n').map(line => ' ' + line);
|
|
356
371
|
}).join('\n'),
|
|
357
|
-
], parserContext._depth,
|
|
372
|
+
], parserContext._depth, furthestChildErrorPosition, parserContext._inputReader.furthestReadPosition, parserContext._inputReader.furthestPeekedPosition, furthestChildErrors);
|
|
358
373
|
}, value, format, ...formatArguments);
|
|
359
374
|
}
|
|
360
375
|
|
|
@@ -396,7 +411,7 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
396
411
|
|
|
397
412
|
return deepestChildError.stack?.split('\n').map(line => ' ' + line);
|
|
398
413
|
}).join('\n'),
|
|
399
|
-
], deepestDepth, parserContext.position, deepestChildErrors);
|
|
414
|
+
], deepestDepth, parserContext.position, parserContext._inputReader.furthestReadPosition, parserContext._inputReader.furthestPeekedPosition, deepestChildErrors);
|
|
400
415
|
}, value, format, ...formatArguments);
|
|
401
416
|
}
|
|
402
417
|
|
|
@@ -417,7 +432,7 @@ export class ParserContextImplementation<Sequence, Element> implements ParserCon
|
|
|
417
432
|
|
|
418
433
|
return childError.stack?.split('\n').map(line => ' ' + line);
|
|
419
434
|
}).join('\n'),
|
|
420
|
-
], parserContext._depth, parserContext.position, childErrors);
|
|
435
|
+
], parserContext._depth, parserContext.position, parserContext._inputReader.furthestReadPosition, parserContext._inputReader.furthestPeekedPosition, childErrors);
|
|
421
436
|
}, value, format, ...formatArguments);
|
|
422
437
|
}
|
|
423
438
|
|
package/src/parserError.ts
CHANGED
|
@@ -23,6 +23,8 @@ type ParserErrorInterface = ParserError;
|
|
|
23
23
|
export interface ParserParsingFailedError extends ParserErrorInterface {
|
|
24
24
|
depth: number;
|
|
25
25
|
position: number;
|
|
26
|
+
furthestReadPosition: number;
|
|
27
|
+
furthestPeekedPosition: number;
|
|
26
28
|
}
|
|
27
29
|
|
|
28
30
|
export function isParserParsingFailedError(value: unknown): value is ParserParsingFailedError {
|
|
@@ -34,6 +36,10 @@ export function isParserParsingFailedError(value: unknown): value is ParserParsi
|
|
|
34
36
|
&& typeof value.depth === 'number'
|
|
35
37
|
&& 'position' in value
|
|
36
38
|
&& typeof value.position === 'number'
|
|
39
|
+
&& 'furthestReadPosition' in value
|
|
40
|
+
&& typeof value.furthestReadPosition === 'number'
|
|
41
|
+
&& 'furthestPeekedPosition' in value
|
|
42
|
+
&& typeof value.furthestPeekedPosition === 'number'
|
|
37
43
|
);
|
|
38
44
|
}
|
|
39
45
|
|
|
@@ -80,6 +86,8 @@ function createParserErrorModule(
|
|
|
80
86
|
message: LazyMessage,
|
|
81
87
|
public readonly depth: number,
|
|
82
88
|
public readonly position: number,
|
|
89
|
+
public readonly furthestReadPosition: number,
|
|
90
|
+
public readonly furthestPeekedPosition: number,
|
|
83
91
|
) {
|
|
84
92
|
super(message);
|
|
85
93
|
}
|
|
@@ -102,9 +110,11 @@ function createParserErrorModule(
|
|
|
102
110
|
message: LazyMessage,
|
|
103
111
|
depth: number,
|
|
104
112
|
position: number,
|
|
113
|
+
furthestReadPosition: number,
|
|
114
|
+
furthestPeekedPosition: number,
|
|
105
115
|
public readonly childErrors: ParserParsingFailedErrorInterface[],
|
|
106
116
|
) {
|
|
107
|
-
super(message, depth, position);
|
|
117
|
+
super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
|
|
@@ -115,9 +125,11 @@ function createParserErrorModule(
|
|
|
115
125
|
message: LazyMessage,
|
|
116
126
|
depth: number,
|
|
117
127
|
position: number,
|
|
128
|
+
furthestReadPosition: number,
|
|
129
|
+
furthestPeekedPosition: number,
|
|
118
130
|
public readonly childErrors: ParserParsingFailedErrorInterface[],
|
|
119
131
|
) {
|
|
120
|
-
super(message, depth, position);
|
|
132
|
+
super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
|
|
121
133
|
}
|
|
122
134
|
}
|
|
123
135
|
|
|
@@ -128,9 +140,11 @@ function createParserErrorModule(
|
|
|
128
140
|
message: LazyMessage,
|
|
129
141
|
depth: number,
|
|
130
142
|
position: number,
|
|
143
|
+
furthestReadPosition: number,
|
|
144
|
+
furthestPeekedPosition: number,
|
|
131
145
|
public readonly childErrors: ParserParsingFailedErrorInterface[],
|
|
132
146
|
) {
|
|
133
|
-
super(message, depth, position);
|
|
147
|
+
super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
|
|
134
148
|
}
|
|
135
149
|
}
|
|
136
150
|
|
package/src/regexpParser.test.ts
CHANGED
|
@@ -184,3 +184,81 @@ test('regexpParser with negative lookahead should match single char', async t =>
|
|
|
184
184
|
t.is(position, 1); // Consumed 1 character
|
|
185
185
|
t.truthy(remainingInput); // There's remaining input (the space)
|
|
186
186
|
});
|
|
187
|
+
|
|
188
|
+
test('regexpParser should match exact 6-character input', async t => {
|
|
189
|
+
const regexpParser = createRegExpParser(/abcdef/);
|
|
190
|
+
|
|
191
|
+
const result = await runParser(
|
|
192
|
+
regexpParser,
|
|
193
|
+
'abcdef',
|
|
194
|
+
stringParserInputCompanion,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
t.is(result[0], 'abcdef');
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('regexpParser should match quoted string of length 6', async t => {
|
|
201
|
+
const regexpParser = createRegExpParser(/"[^"]*"/);
|
|
202
|
+
|
|
203
|
+
const result = await runParser(
|
|
204
|
+
regexpParser,
|
|
205
|
+
'"abcd"', // 6 characters total
|
|
206
|
+
stringParserInputCompanion,
|
|
207
|
+
);
|
|
208
|
+
|
|
209
|
+
t.is(result[0], '"abcd"');
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// Property-based tests for fixed-length patterns
|
|
213
|
+
|
|
214
|
+
testProp(
|
|
215
|
+
'regexpParser should match exact n-character input for any length',
|
|
216
|
+
[fc.integer({ min: 1, max: 20 })],
|
|
217
|
+
async (t, length) => {
|
|
218
|
+
const input = 'a'.repeat(length);
|
|
219
|
+
const regex = new RegExp(`a{${length}}`);
|
|
220
|
+
const regexpParser = createRegExpParser(regex);
|
|
221
|
+
|
|
222
|
+
const result = await runParser(
|
|
223
|
+
regexpParser,
|
|
224
|
+
input,
|
|
225
|
+
stringParserInputCompanion,
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
t.is(result[0], input);
|
|
229
|
+
},
|
|
230
|
+
);
|
|
231
|
+
|
|
232
|
+
testProp(
|
|
233
|
+
'regexpParser should match quoted strings of any length',
|
|
234
|
+
[fc.integer({ min: 0, max: 20 })],
|
|
235
|
+
async (t, contentLength) => {
|
|
236
|
+
const content = 'x'.repeat(contentLength);
|
|
237
|
+
const input = `"${content}"`;
|
|
238
|
+
const regexpParser = createRegExpParser(/"[^"]*"/);
|
|
239
|
+
|
|
240
|
+
const result = await runParser(
|
|
241
|
+
regexpParser,
|
|
242
|
+
input,
|
|
243
|
+
stringParserInputCompanion,
|
|
244
|
+
);
|
|
245
|
+
|
|
246
|
+
t.is(result[0], input);
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
|
|
250
|
+
testProp(
|
|
251
|
+
'regexpParser greedy patterns should match any length input',
|
|
252
|
+
[fc.stringMatching(/^[a-z]+$/).filter(s => s.length > 0 && s.length <= 20)],
|
|
253
|
+
async (t, input) => {
|
|
254
|
+
const regexpParser = createRegExpParser(/[a-z]+/);
|
|
255
|
+
|
|
256
|
+
const result = await runParser(
|
|
257
|
+
regexpParser,
|
|
258
|
+
input,
|
|
259
|
+
stringParserInputCompanion,
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
t.is(result[0], input);
|
|
263
|
+
},
|
|
264
|
+
);
|
package/src/regexpParser.ts
CHANGED
|
@@ -8,17 +8,49 @@ export const createRegExpParser = (
|
|
|
8
8
|
let window = 1;
|
|
9
9
|
let lastMatch: RegExpExecArray | undefined;
|
|
10
10
|
let reachedEndOfInput = false;
|
|
11
|
+
let inputLength: number | undefined;
|
|
11
12
|
|
|
12
13
|
while (true) {
|
|
13
14
|
const sequence = await parserContext.peekSequence(start, start + window);
|
|
14
15
|
|
|
15
16
|
if (sequence === undefined) {
|
|
16
|
-
reachedEndOfInput
|
|
17
|
+
if (!reachedEndOfInput) {
|
|
18
|
+
reachedEndOfInput = true;
|
|
19
|
+
|
|
20
|
+
// Binary search to find actual input length
|
|
21
|
+
// We know: length >= start (we've peeked successfully before)
|
|
22
|
+
// We know: length < start + window (current peek failed)
|
|
23
|
+
let lo = start;
|
|
24
|
+
let hi = start + window;
|
|
25
|
+
while (hi - lo > 1) {
|
|
26
|
+
const mid = (lo + hi) >> 1;
|
|
27
|
+
const probe = await parserContext.peekSequence(0, mid);
|
|
28
|
+
if (probe !== undefined) {
|
|
29
|
+
lo = mid;
|
|
30
|
+
} else {
|
|
31
|
+
hi = mid;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
inputLength = lo;
|
|
35
|
+
|
|
36
|
+
// Try matching against the full input
|
|
37
|
+
if (inputLength > 0) {
|
|
38
|
+
const fullInput = await parserContext.peekSequence(0, inputLength);
|
|
39
|
+
if (fullInput !== undefined) {
|
|
40
|
+
const match = regexp.exec(fullInput);
|
|
41
|
+
if (match !== null && match.index === 0) {
|
|
42
|
+
parserContext.skip(match[0].length);
|
|
43
|
+
return match;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
17
49
|
window = Math.floor(window / 2);
|
|
18
50
|
|
|
19
51
|
if (window === 0) {
|
|
20
52
|
// Get the full sequence we've accumulated to verify matches
|
|
21
|
-
const fullSequence = await parserContext.peekSequence(0, start);
|
|
53
|
+
const fullSequence = await parserContext.peekSequence(0, inputLength ?? start);
|
|
22
54
|
|
|
23
55
|
// Verify any previous match is still valid with full context
|
|
24
56
|
// For lookahead/lookbehind assertions, additional input might invalidate a match
|
|
@@ -46,7 +78,7 @@ export const createRegExpParser = (
|
|
|
46
78
|
continue;
|
|
47
79
|
}
|
|
48
80
|
|
|
49
|
-
const fullSequence = await parserContext.peekSequence(0, start + window);
|
|
81
|
+
const fullSequence = await parserContext.peekSequence(0, inputLength ?? start + window);
|
|
50
82
|
|
|
51
83
|
if (fullSequence === undefined) {
|
|
52
84
|
continue;
|
|
@@ -12,6 +12,7 @@ import { createFixedLengthSequenceParser } from './fixedLengthSequenceParser.js'
|
|
|
12
12
|
import { createTerminatedArrayParser } from './terminatedArrayParser.js';
|
|
13
13
|
import { createDisjunctionParser } from './disjunctionParser.js';
|
|
14
14
|
import { createNegativeLookaheadParser } from './negativeLookaheadParser.js';
|
|
15
|
+
import { createObjectParser } from './objectParser.js';
|
|
15
16
|
import {
|
|
16
17
|
type CharacterSet,
|
|
17
18
|
type CodePointRange,
|
|
@@ -648,20 +649,20 @@ type Quantifier =
|
|
|
648
649
|
| { type: 'optional' }
|
|
649
650
|
| { type: 'repeat'; bounds: RepeatBounds };
|
|
650
651
|
|
|
651
|
-
const starQuantifierParser: Parser<Quantifier, string> =
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
);
|
|
652
|
+
const starQuantifierParser: Parser<Quantifier, string> = createObjectParser({
|
|
653
|
+
type: 'star' as const,
|
|
654
|
+
_marker: createExactSequenceParser('*'),
|
|
655
|
+
});
|
|
655
656
|
|
|
656
|
-
const plusQuantifierParser: Parser<Quantifier, string> =
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
);
|
|
657
|
+
const plusQuantifierParser: Parser<Quantifier, string> = createObjectParser({
|
|
658
|
+
type: 'plus' as const,
|
|
659
|
+
_marker: createExactSequenceParser('+'),
|
|
660
|
+
});
|
|
660
661
|
|
|
661
|
-
const optionalQuantifierParser: Parser<Quantifier, string> =
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
);
|
|
662
|
+
const optionalQuantifierParser: Parser<Quantifier, string> = createObjectParser({
|
|
663
|
+
type: 'optional' as const,
|
|
664
|
+
_marker: createExactSequenceParser('?'),
|
|
665
|
+
});
|
|
665
666
|
|
|
666
667
|
// Parse a number for quantifiers
|
|
667
668
|
const numberParser: Parser<number, string> = parserCreatorCompose(
|
|
@@ -759,24 +760,22 @@ const nonCaptureGroupParser: Parser<RegularExpression, string> = promiseCompose(
|
|
|
759
760
|
type LookaheadMarker = { type: 'lookahead-marker'; isPositive: boolean; inner: RegularExpression };
|
|
760
761
|
|
|
761
762
|
// Positive lookahead (?=...)
|
|
762
|
-
const positiveLookaheadMarkerParser: Parser<LookaheadMarker, string> =
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
);
|
|
763
|
+
const positiveLookaheadMarkerParser: Parser<LookaheadMarker, string> = createObjectParser({
|
|
764
|
+
type: 'lookahead-marker' as const,
|
|
765
|
+
isPositive: true as const,
|
|
766
|
+
_open: createExactSequenceParser('(?='),
|
|
767
|
+
inner: createParserAccessorParser(() => alternationParser),
|
|
768
|
+
_close: createExactSequenceParser(')'),
|
|
769
|
+
});
|
|
770
770
|
|
|
771
771
|
// Negative lookahead (?!...)
|
|
772
|
-
const negativeLookaheadMarkerParser: Parser<LookaheadMarker, string> =
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
);
|
|
772
|
+
const negativeLookaheadMarkerParser: Parser<LookaheadMarker, string> = createObjectParser({
|
|
773
|
+
type: 'lookahead-marker' as const,
|
|
774
|
+
isPositive: false as const,
|
|
775
|
+
_open: createExactSequenceParser('(?!'),
|
|
776
|
+
inner: createParserAccessorParser(() => alternationParser),
|
|
777
|
+
_close: createExactSequenceParser(')'),
|
|
778
|
+
});
|
|
780
779
|
|
|
781
780
|
const groupParser: Parser<RegularExpression, string> = createUnionParser([
|
|
782
781
|
namedCaptureGroupParser,
|
|
@@ -789,15 +788,15 @@ const groupParser: Parser<RegularExpression, string> = createUnionParser([
|
|
|
789
788
|
type AnchorMarker = { type: 'start-anchor-marker' } | { type: 'end-anchor-marker' };
|
|
790
789
|
type ParsedElement = RegularExpression | AnchorMarker | LookaheadMarker;
|
|
791
790
|
|
|
792
|
-
const startAnchorMarkerParser: Parser<AnchorMarker, string> =
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
);
|
|
791
|
+
const startAnchorMarkerParser: Parser<AnchorMarker, string> = createObjectParser({
|
|
792
|
+
type: 'start-anchor-marker' as const,
|
|
793
|
+
_marker: createExactSequenceParser('^'),
|
|
794
|
+
});
|
|
796
795
|
|
|
797
|
-
const endAnchorMarkerParser: Parser<AnchorMarker, string> =
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
);
|
|
796
|
+
const endAnchorMarkerParser: Parser<AnchorMarker, string> = createObjectParser({
|
|
797
|
+
type: 'end-anchor-marker' as const,
|
|
798
|
+
_marker: createExactSequenceParser('$'),
|
|
799
|
+
});
|
|
801
800
|
|
|
802
801
|
// Atom: the basic unit that can be quantified (excluding anchors)
|
|
803
802
|
const atomParser: Parser<RegularExpression, string> = createUnionParser([
|
|
@@ -832,7 +831,7 @@ const quantifiedParser: Parser<RegularExpression, string> = promiseCompose(
|
|
|
832
831
|
);
|
|
833
832
|
|
|
834
833
|
// Element in a sequence: either a quantified atom, anchor marker, or lookahead marker
|
|
835
|
-
const sequenceElementParser: Parser<ParsedElement, string> = createUnionParser
|
|
834
|
+
const sequenceElementParser: Parser<ParsedElement, string> = createUnionParser([
|
|
836
835
|
startAnchorMarkerParser,
|
|
837
836
|
endAnchorMarkerParser,
|
|
838
837
|
positiveLookaheadMarkerParser,
|
|
@@ -21,7 +21,7 @@ export const createSeparatedArrayParser = <ElementOutput, Sequence>(
|
|
|
21
21
|
const elements: ElementOutput[] = [];
|
|
22
22
|
|
|
23
23
|
while (true) {
|
|
24
|
-
|
|
24
|
+
using elementParserContext = parserContext.lookahead();
|
|
25
25
|
const initialPosition = elementParserContext.position;
|
|
26
26
|
|
|
27
27
|
try {
|
|
@@ -39,8 +39,6 @@ export const createSeparatedArrayParser = <ElementOutput, Sequence>(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
throw error;
|
|
42
|
-
} finally {
|
|
43
|
-
elementParserContext.dispose();
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
parser = separatorThenElementParser;
|
|
@@ -21,7 +21,7 @@ export const createSeparatedNonEmptyArrayParser = <ElementOutput, Sequence>(
|
|
|
21
21
|
const elements: ElementOutput[] = [];
|
|
22
22
|
|
|
23
23
|
while (true) {
|
|
24
|
-
|
|
24
|
+
using elementParserContext = parserContext.lookahead();
|
|
25
25
|
const initialPosition = elementParserContext.position;
|
|
26
26
|
|
|
27
27
|
try {
|
|
@@ -39,8 +39,6 @@ export const createSeparatedNonEmptyArrayParser = <ElementOutput, Sequence>(
|
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
throw error;
|
|
42
|
-
} finally {
|
|
43
|
-
elementParserContext.dispose();
|
|
44
42
|
}
|
|
45
43
|
|
|
46
44
|
parser = separatorThenElementParser;
|
|
@@ -32,7 +32,7 @@ test('sliceBoundedParser', async t => {
|
|
|
32
32
|
test('sliceBoundedParser mustConsumeAll: true fail to cosume all', async t => {
|
|
33
33
|
const parser = createTupleParser([
|
|
34
34
|
createElementParser<string>(),
|
|
35
|
-
createSliceBoundedParser(createArrayParser(createExactElementParser('b'
|
|
35
|
+
createSliceBoundedParser(createArrayParser(createExactElementParser<string>('b')), 2),
|
|
36
36
|
createElementParser(),
|
|
37
37
|
]);
|
|
38
38
|
|
|
@@ -45,7 +45,7 @@ test('sliceBoundedParser mustConsumeAll: true fail to cosume all', async t => {
|
|
|
45
45
|
test('sliceBoundedParser mustConsumeAll: false', async t => {
|
|
46
46
|
const parser = createTupleParser([
|
|
47
47
|
createElementParser<string>(),
|
|
48
|
-
createSliceBoundedParser(createArrayParser(createExactElementParser('b'
|
|
48
|
+
createSliceBoundedParser(createArrayParser(createExactElementParser<string>('b')), 2, false),
|
|
49
49
|
createElementParser(),
|
|
50
50
|
]);
|
|
51
51
|
|
|
@@ -8,30 +8,26 @@ export const createSliceBoundedParser = <Output, Sequence>(
|
|
|
8
8
|
const sliceBoundedParser: typeof childParser = async parserContext => {
|
|
9
9
|
const absoluteSliceEnd = parserContext.position + sliceEnd;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
using childParserContext = parserContext.lookahead({
|
|
12
12
|
sliceEnd: absoluteSliceEnd,
|
|
13
13
|
});
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
const value = await childParser(childParserContext);
|
|
15
|
+
const value = await childParser(childParserContext);
|
|
17
16
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
childParserContext.invariant(
|
|
18
|
+
(
|
|
19
|
+
!mustConsumeAll
|
|
20
|
+
|| childParserContext.position === absoluteSliceEnd
|
|
21
|
+
),
|
|
22
|
+
'child parser must consume all input in the slice (%s in total, up to position %s), instead consumed %s up to position %s',
|
|
23
|
+
sliceEnd,
|
|
24
|
+
absoluteSliceEnd,
|
|
25
|
+
childParserContext.position - parserContext.position,
|
|
26
|
+
childParserContext.position,
|
|
27
|
+
);
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} finally {
|
|
33
|
-
childParserContext.dispose();
|
|
34
|
-
}
|
|
29
|
+
childParserContext.unlookahead();
|
|
30
|
+
return value;
|
|
35
31
|
};
|
|
36
32
|
|
|
37
33
|
setParserName(sliceBoundedParser, [
|
package/src/smaliParser.test.ts
CHANGED
|
@@ -441,3 +441,67 @@ test('parse smali with virtual method after direct methods (a0/n issue)', async
|
|
|
441
441
|
t.truthy(actual);
|
|
442
442
|
t.is(actual.classData?.virtualMethods.length, 1);
|
|
443
443
|
});
|
|
444
|
+
|
|
445
|
+
// Minimal failing test case for .end local directive
|
|
446
|
+
test('parse smali with .end local directive', async t => {
|
|
447
|
+
const smali = `.class public Lpl/czak/minimal/MainActivity;
|
|
448
|
+
.super Landroid/app/Activity;
|
|
449
|
+
|
|
450
|
+
# direct methods
|
|
451
|
+
.method private sumToN(I)I
|
|
452
|
+
.registers 4
|
|
453
|
+
.param p1, "n" # I
|
|
454
|
+
|
|
455
|
+
const/4 v0, 0x0
|
|
456
|
+
|
|
457
|
+
.local v0, "sum":I
|
|
458
|
+
const/4 v1, 0x1
|
|
459
|
+
|
|
460
|
+
.local v1, "i":I
|
|
461
|
+
:goto_2
|
|
462
|
+
if-gt v1, p1, :cond_8
|
|
463
|
+
|
|
464
|
+
add-int/2addr v0, v1
|
|
465
|
+
|
|
466
|
+
add-int/lit8 v1, v1, 0x1
|
|
467
|
+
|
|
468
|
+
goto :goto_2
|
|
469
|
+
|
|
470
|
+
.end local v1 # "i":I
|
|
471
|
+
:cond_8
|
|
472
|
+
return v0
|
|
473
|
+
.end method
|
|
474
|
+
`;
|
|
475
|
+
|
|
476
|
+
const actual = await runParser(smaliParser, smali, stringParserInputCompanion, {
|
|
477
|
+
errorJoinMode: 'all',
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
t.truthy(actual);
|
|
481
|
+
t.is(actual.classData?.directMethods.length, 1);
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
// Minimal test case for .local with generic type signature
|
|
485
|
+
test('parse smali with .local directive with generic type signature', async t => {
|
|
486
|
+
const smali = `.class public Lpl/czak/minimal/MainActivity;
|
|
487
|
+
.super Landroid/app/Activity;
|
|
488
|
+
|
|
489
|
+
# direct methods
|
|
490
|
+
.method private useLambda()Ljava/lang/String;
|
|
491
|
+
.registers 3
|
|
492
|
+
|
|
493
|
+
new-instance v0, Lpl/czak/minimal/MainActivity$$ExternalSyntheticLambda0;
|
|
494
|
+
|
|
495
|
+
.local v0, "supplier":Ljava/util/function/Supplier;, "Ljava/util/function/Supplier<Ljava/lang/String;>;"
|
|
496
|
+
|
|
497
|
+
return-object v0
|
|
498
|
+
.end method
|
|
499
|
+
`;
|
|
500
|
+
|
|
501
|
+
const actual = await runParser(smaliParser, smali, stringParserInputCompanion, {
|
|
502
|
+
errorJoinMode: 'all',
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
t.truthy(actual);
|
|
506
|
+
t.is(actual.classData?.directMethods.length, 1);
|
|
507
|
+
});
|