@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.
Files changed (262) hide show
  1. package/.claude/settings.local.json +24 -0
  2. package/.github/workflows/main.yml +1 -0
  3. package/build/androidPackageParser.js +30 -32
  4. package/build/arbitraryDalvikBytecode.d.ts +3 -3
  5. package/build/arbitraryDalvikBytecode.js +33 -27
  6. package/build/arbitraryDalvikExecutable.js +55 -17
  7. package/build/arbitraryJava.d.ts +31 -0
  8. package/build/arbitraryJava.js +532 -0
  9. package/build/arbitraryJavaScript.d.ts +3 -0
  10. package/build/arbitraryJavaScript.js +263 -0
  11. package/build/arbitraryJavascript.d.ts +3 -0
  12. package/build/arbitraryJavascript.js +263 -0
  13. package/build/arbitraryZig.d.ts +3 -0
  14. package/build/arbitraryZig.js +240 -0
  15. package/build/arbitraryZipStream.d.ts +1 -1
  16. package/build/arrayParser.js +72 -13
  17. package/build/backsmali.d.ts +4 -3
  18. package/build/backsmali.js +26 -6
  19. package/build/bash.d.ts +89 -0
  20. package/build/bash.js +1 -0
  21. package/build/bashParser.d.ts +6 -0
  22. package/build/bashParser.js +335 -0
  23. package/build/bashParser.test.d.ts +1 -0
  24. package/build/bashParser.test.js +343 -0
  25. package/build/bashParserEdgeCases.test.d.ts +1 -0
  26. package/build/bashParserEdgeCases.test.js +117 -0
  27. package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
  28. package/build/dalvikBytecodeParser/addressConversion.js +334 -0
  29. package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
  30. package/build/dalvikBytecodeParser/formatParsers.js +13 -14
  31. package/build/dalvikBytecodeParser.d.ts +60 -31
  32. package/build/dalvikBytecodeParser.js +92 -35
  33. package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
  34. package/build/dalvikBytecodeParser.test-d.js +268 -0
  35. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
  36. package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
  37. package/build/dalvikBytecodeUnparser.d.ts +2 -2
  38. package/build/dalvikBytecodeUnparser.js +23 -23
  39. package/build/dalvikBytecodeUnparser.test.js +7 -7
  40. package/build/dalvikExecutable.d.ts +3 -3
  41. package/build/dalvikExecutable.test-d.d.ts +1 -0
  42. package/build/dalvikExecutable.test-d.js +59 -0
  43. package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
  44. package/build/dalvikExecutableParser/typedNumbers.js +3 -0
  45. package/build/dalvikExecutableParser.d.ts +2 -1
  46. package/build/dalvikExecutableParser.js +96 -77
  47. package/build/dalvikExecutableParser.test.js +24 -3
  48. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
  49. package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
  50. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
  51. package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
  52. package/build/dalvikExecutableUnparser.d.ts +2 -2
  53. package/build/dalvikExecutableUnparser.test.js +2 -1
  54. package/build/disjunctionParser.d.ts +5 -3
  55. package/build/disjunctionParser.js +79 -17
  56. package/build/disjunctionParser.test-d.d.ts +1 -0
  57. package/build/disjunctionParser.test-d.js +72 -0
  58. package/build/elementSwitchParser.d.ts +4 -0
  59. package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
  60. package/build/elementSwitchParser.test-d.d.ts +1 -0
  61. package/build/elementSwitchParser.test-d.js +44 -0
  62. package/build/exactSequenceParser.d.ts +4 -2
  63. package/build/exactSequenceParser.test-d.d.ts +1 -0
  64. package/build/exactSequenceParser.test-d.js +36 -0
  65. package/build/fetchCid.js +2 -66
  66. package/build/index.d.ts +25 -2
  67. package/build/index.js +23 -1
  68. package/build/index.test.js +16 -1
  69. package/build/inputReader.d.ts +10 -0
  70. package/build/inputReader.js +36 -0
  71. package/build/java.d.ts +502 -0
  72. package/build/java.js +2 -0
  73. package/build/javaKeyStoreParser.js +14 -17
  74. package/build/javaParser.d.ts +51 -0
  75. package/build/javaParser.js +1538 -0
  76. package/build/javaParser.test.d.ts +1 -0
  77. package/build/javaParser.test.js +1287 -0
  78. package/build/javaScript.d.ts +35 -0
  79. package/build/javaScript.js +1 -0
  80. package/build/javaScriptParser.d.ts +9 -0
  81. package/build/javaScriptParser.js +34 -0
  82. package/build/javaScriptUnparser.d.ts +3 -0
  83. package/build/javaScriptUnparser.js +4 -0
  84. package/build/javaScriptUnparser.test.d.ts +1 -0
  85. package/build/javaScriptUnparser.test.js +24 -0
  86. package/build/javaUnparser.d.ts +2 -0
  87. package/build/javaUnparser.js +519 -0
  88. package/build/javaUnparser.test.d.ts +1 -0
  89. package/build/javaUnparser.test.js +24 -0
  90. package/build/javascript.d.ts +35 -0
  91. package/build/javascript.js +1 -0
  92. package/build/javascriptParser.d.ts +9 -0
  93. package/build/javascriptParser.js +34 -0
  94. package/build/javascriptUnparser.d.ts +3 -0
  95. package/build/javascriptUnparser.js +4 -0
  96. package/build/javascriptUnparser.test.d.ts +1 -0
  97. package/build/javascriptUnparser.test.js +24 -0
  98. package/build/jsonParser.js +2 -12
  99. package/build/lazyMessageError.d.ts +3 -0
  100. package/build/lookaheadParser.js +60 -3
  101. package/build/negativeLookaheadParser.js +70 -11
  102. package/build/nonEmptyArrayParser.js +72 -13
  103. package/build/objectParser.d.ts +12 -0
  104. package/build/objectParser.js +31 -0
  105. package/build/objectParser.test-d.d.ts +1 -0
  106. package/build/objectParser.test-d.js +112 -0
  107. package/build/objectParser.test.d.ts +1 -0
  108. package/build/objectParser.test.js +55 -0
  109. package/build/optionalParser.js +69 -10
  110. package/build/parser.d.ts +4 -0
  111. package/build/parser.js +3 -1
  112. package/build/parser.test.js +114 -1
  113. package/build/parserConsumedSequenceParser.js +66 -7
  114. package/build/parserContext.d.ts +6 -0
  115. package/build/parserContext.js +20 -11
  116. package/build/parserError.d.ts +119 -27
  117. package/build/parserError.js +16 -8
  118. package/build/regexpParser.d.ts +2 -0
  119. package/build/regexpParser.js +101 -0
  120. package/build/regexpParser.test.d.ts +1 -0
  121. package/build/regexpParser.test.js +114 -0
  122. package/build/regularExpression.d.ts +63 -0
  123. package/build/regularExpression.js +1 -0
  124. package/build/regularExpressionParser.d.ts +3 -0
  125. package/build/regularExpressionParser.js +600 -0
  126. package/build/regularExpressionParser.test.d.ts +1 -0
  127. package/build/regularExpressionParser.test.js +89 -0
  128. package/build/separatedArrayParser.js +73 -14
  129. package/build/separatedNonEmptyArrayParser.js +73 -14
  130. package/build/sliceBoundedParser.js +62 -5
  131. package/build/smaliParser.d.ts +7 -7
  132. package/build/smaliParser.js +185 -268
  133. package/build/smaliParser.test.js +58 -0
  134. package/build/stringEscapes.d.ts +5 -0
  135. package/build/stringEscapes.js +244 -0
  136. package/build/symbolicExpression.d.ts +29 -0
  137. package/build/symbolicExpression.js +1 -0
  138. package/build/symbolicExpressionParser.d.ts +4 -0
  139. package/build/symbolicExpressionParser.js +123 -0
  140. package/build/symbolicExpressionParser.test.d.ts +1 -0
  141. package/build/symbolicExpressionParser.test.js +289 -0
  142. package/build/terminatedArrayParser.js +113 -38
  143. package/build/terminatedArrayParser.test.js +4 -2
  144. package/build/tupleParser.d.ts +7 -15
  145. package/build/tupleParser.js +1 -0
  146. package/build/unionParser.d.ts +5 -3
  147. package/build/unionParser.js +7 -2
  148. package/build/unionParser.test-d.d.ts +1 -0
  149. package/build/unionParser.test-d.js +72 -0
  150. package/build/unionParser.test.js +10 -11
  151. package/build/zig.d.ts +280 -0
  152. package/build/zig.js +2 -0
  153. package/build/zigParser.d.ts +3 -0
  154. package/build/zigParser.js +1119 -0
  155. package/build/zigParser.test.d.ts +1 -0
  156. package/build/zigParser.test.js +1590 -0
  157. package/build/zigUnparser.d.ts +2 -0
  158. package/build/zigUnparser.js +460 -0
  159. package/build/zigUnparser.test.d.ts +1 -0
  160. package/build/zigUnparser.test.js +24 -0
  161. package/build/zipParser.js +19 -32
  162. package/build/zipUnparser.js +19 -7
  163. package/build/zipUnparser.test.js +1 -1
  164. package/node_modules-@types/s-expression/index.d.ts +5 -0
  165. package/package.json +25 -6
  166. package/src/androidPackageParser.ts +33 -60
  167. package/src/arbitraryDalvikBytecode.ts +39 -31
  168. package/src/arbitraryDalvikExecutable.ts +65 -20
  169. package/src/arbitraryJava.ts +804 -0
  170. package/src/arbitraryJavaScript.ts +410 -0
  171. package/src/arbitraryZig.ts +380 -0
  172. package/src/arrayParser.ts +1 -3
  173. package/src/backsmali.ts +35 -4
  174. package/src/bash.ts +127 -0
  175. package/src/bashParser.test.ts +590 -0
  176. package/src/bashParser.ts +498 -0
  177. package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
  178. package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
  179. package/src/dalvikBytecodeParser.test-d.ts +310 -0
  180. package/src/dalvikBytecodeParser.ts +194 -69
  181. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
  182. package/src/dalvikBytecodeUnparser.test.ts +7 -7
  183. package/src/dalvikBytecodeUnparser.ts +31 -30
  184. package/src/dalvikExecutable.test-d.ts +132 -0
  185. package/src/dalvikExecutable.ts +3 -3
  186. package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
  187. package/src/dalvikExecutableParser.test.ts +37 -3
  188. package/src/dalvikExecutableParser.test.ts.md +163 -2
  189. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  190. package/src/dalvikExecutableParser.ts +121 -139
  191. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
  192. package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
  193. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
  194. package/src/dalvikExecutableUnparser.test.ts +3 -2
  195. package/src/dalvikExecutableUnparser.ts +4 -4
  196. package/src/disjunctionParser.test-d.ts +105 -0
  197. package/src/disjunctionParser.ts +18 -15
  198. package/src/elementSwitchParser.test-d.ts +74 -0
  199. package/src/elementSwitchParser.ts +51 -0
  200. package/src/exactSequenceParser.test-d.ts +43 -0
  201. package/src/exactSequenceParser.ts +13 -8
  202. package/src/fetchCid.ts +2 -76
  203. package/src/index.test.ts +22 -1
  204. package/src/index.ts +119 -2
  205. package/src/inputReader.ts +53 -0
  206. package/src/java.ts +708 -0
  207. package/src/javaKeyStoreParser.ts +18 -32
  208. package/src/javaParser.test.ts +1592 -0
  209. package/src/javaParser.ts +2640 -0
  210. package/src/javaScript.ts +36 -0
  211. package/src/javaScriptParser.ts +57 -0
  212. package/src/javaScriptUnparser.test.ts +37 -0
  213. package/src/javaScriptUnparser.ts +7 -0
  214. package/src/javaUnparser.test.ts +37 -0
  215. package/src/javaUnparser.ts +640 -0
  216. package/src/jsonParser.ts +6 -27
  217. package/src/lookaheadParser.ts +2 -6
  218. package/src/negativeLookaheadParser.ts +1 -3
  219. package/src/nonEmptyArrayParser.ts +1 -3
  220. package/src/objectParser.test-d.ts +152 -0
  221. package/src/objectParser.test.ts +71 -0
  222. package/src/objectParser.ts +69 -0
  223. package/src/optionalParser.ts +1 -3
  224. package/src/parser.test.ts +151 -4
  225. package/src/parser.ts +11 -1
  226. package/src/parserConsumedSequenceParser.ts +2 -4
  227. package/src/parserContext.ts +26 -11
  228. package/src/parserError.ts +17 -3
  229. package/src/regexpParser.test.ts +264 -0
  230. package/src/regexpParser.ts +126 -0
  231. package/src/regularExpression.ts +24 -0
  232. package/src/regularExpressionParser.test.ts +102 -0
  233. package/src/regularExpressionParser.ts +920 -0
  234. package/src/separatedArrayParser.ts +1 -3
  235. package/src/separatedNonEmptyArrayParser.ts +1 -3
  236. package/src/sliceBoundedParser.test.ts +2 -2
  237. package/src/sliceBoundedParser.ts +15 -19
  238. package/src/smaliParser.test.ts +64 -0
  239. package/src/smaliParser.test.ts.md +12 -12
  240. package/src/smaliParser.test.ts.snap +0 -0
  241. package/src/smaliParser.ts +246 -534
  242. package/src/stringEscapes.ts +253 -0
  243. package/src/symbolicExpression.ts +17 -0
  244. package/src/symbolicExpressionParser.test.ts +466 -0
  245. package/src/symbolicExpressionParser.ts +190 -0
  246. package/src/terminatedArrayParser.test.ts +9 -6
  247. package/src/terminatedArrayParser.ts +25 -29
  248. package/src/tupleParser.ts +21 -18
  249. package/src/unionParser.test-d.ts +105 -0
  250. package/src/unionParser.test.ts +18 -17
  251. package/src/unionParser.ts +28 -16
  252. package/src/zig.ts +411 -0
  253. package/src/zigParser.test.ts +1693 -0
  254. package/src/zigParser.ts +1745 -0
  255. package/src/zigUnparser.test.ts +37 -0
  256. package/src/zigUnparser.ts +615 -0
  257. package/src/zipParser.ts +20 -56
  258. package/src/zipUnparser.test.ts +1 -1
  259. package/src/zipUnparser.ts +22 -7
  260. package/tsconfig.json +2 -2
  261. package/build/exactElementSwitchParser.d.ts +0 -3
  262. package/src/exactElementSwitchParser.ts +0 -41
@@ -15,7 +15,11 @@ export function isParserParsingFailedError(value) {
15
15
  && 'depth' in value
16
16
  && typeof value.depth === 'number'
17
17
  && 'position' in value
18
- && typeof value.position === 'number');
18
+ && typeof value.position === 'number'
19
+ && 'furthestReadPosition' in value
20
+ && typeof value.furthestReadPosition === 'number'
21
+ && 'furthestPeekedPosition' in value
22
+ && typeof value.furthestPeekedPosition === 'number');
19
23
  }
20
24
  export function isParserParsingJoinError(value) {
21
25
  return (typeof value === 'object'
@@ -40,11 +44,15 @@ function createParserErrorModule(BaseLazyMessageError) {
40
44
  class ParserParsingFailedError extends ParserError {
41
45
  depth;
42
46
  position;
47
+ furthestReadPosition;
48
+ furthestPeekedPosition;
43
49
  name = 'ParserParsingFailedError';
44
- constructor(message, depth, position) {
50
+ constructor(message, depth, position, furthestReadPosition, furthestPeekedPosition) {
45
51
  super(message);
46
52
  this.depth = depth;
47
53
  this.position = position;
54
+ this.furthestReadPosition = furthestReadPosition;
55
+ this.furthestPeekedPosition = furthestPeekedPosition;
48
56
  }
49
57
  }
50
58
  class ParserParsingJoinError extends ParserParsingFailedError {
@@ -57,24 +65,24 @@ function createParserErrorModule(BaseLazyMessageError) {
57
65
  class ParserParsingJoinAllError extends ParserParsingJoinError {
58
66
  childErrors;
59
67
  name = 'ParserParsingJoinAllError';
60
- constructor(message, depth, position, childErrors) {
61
- super(message, depth, position);
68
+ constructor(message, depth, position, furthestReadPosition, furthestPeekedPosition, childErrors) {
69
+ super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
62
70
  this.childErrors = childErrors;
63
71
  }
64
72
  }
65
73
  class ParserParsingJoinDeepestError extends ParserParsingJoinError {
66
74
  childErrors;
67
75
  name = 'ParserParsingJoinDeepestError';
68
- constructor(message, depth, position, childErrors) {
69
- super(message, depth, position);
76
+ constructor(message, depth, position, furthestReadPosition, furthestPeekedPosition, childErrors) {
77
+ super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
70
78
  this.childErrors = childErrors;
71
79
  }
72
80
  }
73
81
  class ParserParsingJoinFurthestError extends ParserParsingJoinError {
74
82
  childErrors;
75
83
  name = 'ParserParsingJoinFurthestError';
76
- constructor(message, depth, position, childErrors) {
77
- super(message, depth, position);
84
+ constructor(message, depth, position, furthestReadPosition, furthestPeekedPosition, childErrors) {
85
+ super(message, depth, position, furthestReadPosition, furthestPeekedPosition);
78
86
  this.childErrors = childErrors;
79
87
  }
80
88
  }
@@ -0,0 +1,2 @@
1
+ import { type Parser } from './parser.js';
2
+ export declare const createRegExpParser: (regexp: RegExp) => Parser<RegExpExecArray, string, string>;
@@ -0,0 +1,101 @@
1
+ import { setParserName } from './parser.js';
2
+ export const createRegExpParser = (regexp) => {
3
+ const regexpParser = async (parserContext) => {
4
+ let start = 0;
5
+ let window = 1;
6
+ let lastMatch;
7
+ let reachedEndOfInput = false;
8
+ let inputLength;
9
+ while (true) {
10
+ const sequence = await parserContext.peekSequence(start, start + window);
11
+ if (sequence === undefined) {
12
+ if (!reachedEndOfInput) {
13
+ reachedEndOfInput = true;
14
+ // Binary search to find actual input length
15
+ // We know: length >= start (we've peeked successfully before)
16
+ // We know: length < start + window (current peek failed)
17
+ let lo = start;
18
+ let hi = start + window;
19
+ while (hi - lo > 1) {
20
+ const mid = (lo + hi) >> 1;
21
+ const probe = await parserContext.peekSequence(0, mid);
22
+ if (probe !== undefined) {
23
+ lo = mid;
24
+ }
25
+ else {
26
+ hi = mid;
27
+ }
28
+ }
29
+ inputLength = lo;
30
+ // Try matching against the full input
31
+ if (inputLength > 0) {
32
+ const fullInput = await parserContext.peekSequence(0, inputLength);
33
+ if (fullInput !== undefined) {
34
+ const match = regexp.exec(fullInput);
35
+ if (match !== null && match.index === 0) {
36
+ parserContext.skip(match[0].length);
37
+ return match;
38
+ }
39
+ }
40
+ }
41
+ }
42
+ window = Math.floor(window / 2);
43
+ if (window === 0) {
44
+ // Get the full sequence we've accumulated to verify matches
45
+ const fullSequence = await parserContext.peekSequence(0, inputLength ?? start);
46
+ // Verify any previous match is still valid with full context
47
+ // For lookahead/lookbehind assertions, additional input might invalidate a match
48
+ if (fullSequence !== undefined) {
49
+ const verifyMatch = regexp.exec(fullSequence);
50
+ if (verifyMatch !== null && verifyMatch.index === 0) {
51
+ parserContext.skip(verifyMatch[0].length);
52
+ return verifyMatch;
53
+ }
54
+ }
55
+ else if (lastMatch !== undefined) {
56
+ // No full sequence available but we have a previous match
57
+ parserContext.skip(lastMatch[0].length);
58
+ return lastMatch;
59
+ }
60
+ // No previous match - try matching against empty string for zero-width patterns (e.g., /a*/, /[ \t]*/)
61
+ const emptyMatch = regexp.exec('');
62
+ if (emptyMatch !== null && emptyMatch.index === 0) {
63
+ return emptyMatch;
64
+ }
65
+ return parserContext.invariant(false, 'Unexpected end of input without regex match');
66
+ }
67
+ continue;
68
+ }
69
+ const fullSequence = await parserContext.peekSequence(0, inputLength ?? start + window);
70
+ if (fullSequence === undefined) {
71
+ continue;
72
+ }
73
+ const match = regexp.exec(fullSequence);
74
+ if (match === null || match.index !== 0) {
75
+ if (lastMatch !== undefined) {
76
+ // Verify lastMatch is still valid with current full context
77
+ // For lookahead/lookbehind assertions, a match on shorter input might be
78
+ // invalidated by additional input (e.g., /\|(?!\|)/ matches '|' but not '||')
79
+ const verifyMatch = regexp.exec(fullSequence);
80
+ if (verifyMatch !== null && verifyMatch.index === 0) {
81
+ parserContext.skip(verifyMatch[0].length);
82
+ return verifyMatch;
83
+ }
84
+ // lastMatch was invalidated by additional context
85
+ lastMatch = undefined;
86
+ }
87
+ if (reachedEndOfInput) {
88
+ parserContext.invariant(false, 'Regex did not match at start of input');
89
+ }
90
+ start += window;
91
+ window *= 2;
92
+ continue;
93
+ }
94
+ lastMatch = match;
95
+ start += window;
96
+ window *= 2;
97
+ }
98
+ };
99
+ setParserName(regexpParser, regexp.toString());
100
+ return regexpParser;
101
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,114 @@
1
+ import test from 'ava';
2
+ import * as fc from 'fast-check';
3
+ import { testProp } from '@fast-check/ava';
4
+ import { runParser, runParserWithRemainingInput } from './parser.js';
5
+ import { stringParserInputCompanion } from './parserInputCompanion.js';
6
+ import { createRegExpParser } from './regexpParser.js';
7
+ test('regexpParser matches digits', async (t) => {
8
+ const regexpParser = createRegExpParser(/\d+/);
9
+ const result = await runParser(regexpParser, '123', stringParserInputCompanion);
10
+ t.is(result[0], '123');
11
+ });
12
+ test('regexpParser matches at start only', async (t) => {
13
+ const regexpParser = createRegExpParser(/\d+/);
14
+ const { output, remainingInput } = await runParserWithRemainingInput(regexpParser, '123abc', stringParserInputCompanion);
15
+ t.is(output[0], '123');
16
+ t.truthy(remainingInput);
17
+ });
18
+ test('regexpParser fails when no match at start', async (t) => {
19
+ const regexpParser = createRegExpParser(/\d+/);
20
+ await t.throwsAsync(runParser(regexpParser, 'abc123', stringParserInputCompanion));
21
+ });
22
+ test('regexpParser with capture groups', async (t) => {
23
+ const regexpParser = createRegExpParser(/(\d+)-(\d+)/);
24
+ const result = await runParser(regexpParser, '123-456', stringParserInputCompanion);
25
+ t.is(result[0], '123-456');
26
+ t.is(result[1], '123');
27
+ t.is(result[2], '456');
28
+ });
29
+ test('regexpParser greedy matching', async (t) => {
30
+ const regexpParser = createRegExpParser(/a+/);
31
+ const { output } = await runParserWithRemainingInput(regexpParser, 'aaab', stringParserInputCompanion);
32
+ t.is(output[0], 'aaa');
33
+ });
34
+ test('regexpParser with anchored regexp', async (t) => {
35
+ const regexpParser = createRegExpParser(/^hello/);
36
+ const { output } = await runParserWithRemainingInput(regexpParser, 'hello world', stringParserInputCompanion);
37
+ t.is(output[0], 'hello');
38
+ });
39
+ testProp.serial('regexpParser matches word characters', [
40
+ fc.tuple(fc.stringMatching(/^\w+$/), fc.stringMatching(/^\W*$/)),
41
+ ], async (t, [word, nonWord]) => {
42
+ const regexpParser = createRegExpParser(/\w+/);
43
+ const { output, position } = await runParserWithRemainingInput(regexpParser, word + nonWord, stringParserInputCompanion);
44
+ t.is(output[0], word);
45
+ t.is(position, word.length);
46
+ }, {
47
+ verbose: true,
48
+ });
49
+ // Tests for zero-width/optional patterns at end of input
50
+ test('regexpParser with star quantifier on empty input', async (t) => {
51
+ const regexpParser = createRegExpParser(/a*/);
52
+ const result = await runParser(regexpParser, '', stringParserInputCompanion);
53
+ t.is(result[0], '');
54
+ });
55
+ test('regexpParser with optional whitespace on empty input', async (t) => {
56
+ const regexpParser = createRegExpParser(/[ \t]*/);
57
+ const result = await runParser(regexpParser, '', stringParserInputCompanion);
58
+ t.is(result[0], '');
59
+ });
60
+ test('regexpParser with star quantifier at end of input (no match)', async (t) => {
61
+ const regexpParser = createRegExpParser(/a*/);
62
+ const { output } = await runParserWithRemainingInput(regexpParser, 'bbb', stringParserInputCompanion);
63
+ t.is(output[0], '');
64
+ });
65
+ test('regexpParser with optional group on empty input', async (t) => {
66
+ const regexpParser = createRegExpParser(/(?:foo)?/);
67
+ const result = await runParser(regexpParser, '', stringParserInputCompanion);
68
+ t.is(result[0], '');
69
+ });
70
+ // Tests for negative lookahead
71
+ test('regexpParser with negative lookahead should not match when followed by same char', async (t) => {
72
+ // This regex should NOT match anything in '||' - the | is followed by another |
73
+ const regexpParser = createRegExpParser(/\|(?!\|)/);
74
+ await t.throwsAsync(runParser(regexpParser, '||', stringParserInputCompanion));
75
+ });
76
+ test('regexpParser with negative lookahead should match single char', async (t) => {
77
+ // This regex should match single '|' when followed by something else
78
+ const regexpParser = createRegExpParser(/\|(?!\|)/);
79
+ const { output, position, remainingInput } = await runParserWithRemainingInput(regexpParser, '| ', stringParserInputCompanion);
80
+ t.is(output[0], '|');
81
+ t.is(position, 1); // Consumed 1 character
82
+ t.truthy(remainingInput); // There's remaining input (the space)
83
+ });
84
+ test('regexpParser should match exact 6-character input', async (t) => {
85
+ const regexpParser = createRegExpParser(/abcdef/);
86
+ const result = await runParser(regexpParser, 'abcdef', stringParserInputCompanion);
87
+ t.is(result[0], 'abcdef');
88
+ });
89
+ test('regexpParser should match quoted string of length 6', async (t) => {
90
+ const regexpParser = createRegExpParser(/"[^"]*"/);
91
+ const result = await runParser(regexpParser, '"abcd"', // 6 characters total
92
+ stringParserInputCompanion);
93
+ t.is(result[0], '"abcd"');
94
+ });
95
+ // Property-based tests for fixed-length patterns
96
+ testProp('regexpParser should match exact n-character input for any length', [fc.integer({ min: 1, max: 20 })], async (t, length) => {
97
+ const input = 'a'.repeat(length);
98
+ const regex = new RegExp(`a{${length}}`);
99
+ const regexpParser = createRegExpParser(regex);
100
+ const result = await runParser(regexpParser, input, stringParserInputCompanion);
101
+ t.is(result[0], input);
102
+ });
103
+ testProp('regexpParser should match quoted strings of any length', [fc.integer({ min: 0, max: 20 })], async (t, contentLength) => {
104
+ const content = 'x'.repeat(contentLength);
105
+ const input = `"${content}"`;
106
+ const regexpParser = createRegExpParser(/"[^"]*"/);
107
+ const result = await runParser(regexpParser, input, stringParserInputCompanion);
108
+ t.is(result[0], input);
109
+ });
110
+ testProp('regexpParser greedy patterns should match any length input', [fc.stringMatching(/^[a-z]+$/).filter(s => s.length > 0 && s.length <= 20)], async (t, input) => {
111
+ const regexpParser = createRegExpParser(/[a-z]+/);
112
+ const result = await runParser(regexpParser, input, stringParserInputCompanion);
113
+ t.is(result[0], input);
114
+ });
@@ -0,0 +1,63 @@
1
+ export type CodePointRange = {
2
+ start: number;
3
+ end: number;
4
+ };
5
+ export type CharacterSet = {
6
+ type: 'empty';
7
+ } | {
8
+ type: 'node';
9
+ range: CodePointRange;
10
+ left: CharacterSet;
11
+ right: CharacterSet;
12
+ };
13
+ export type RepeatBounds = number | {
14
+ min: number;
15
+ max?: number;
16
+ } | {
17
+ min?: number;
18
+ max: number;
19
+ };
20
+ export type RegularExpression = {
21
+ type: 'epsilon';
22
+ } | {
23
+ type: 'literal';
24
+ charset: CharacterSet;
25
+ } | {
26
+ type: 'concat';
27
+ left: RegularExpression;
28
+ right: RegularExpression;
29
+ } | {
30
+ type: 'union';
31
+ left: RegularExpression;
32
+ right: RegularExpression;
33
+ } | {
34
+ type: 'star';
35
+ inner: RegularExpression;
36
+ } | {
37
+ type: 'plus';
38
+ inner: RegularExpression;
39
+ } | {
40
+ type: 'optional';
41
+ inner: RegularExpression;
42
+ } | {
43
+ type: 'repeat';
44
+ inner: RegularExpression;
45
+ bounds: RepeatBounds;
46
+ } | {
47
+ type: 'capture-group';
48
+ inner: RegularExpression;
49
+ name?: string;
50
+ } | {
51
+ type: 'lookahead';
52
+ isPositive: boolean;
53
+ inner: RegularExpression;
54
+ right: RegularExpression;
55
+ } | {
56
+ type: 'start-anchor';
57
+ left: RegularExpression;
58
+ right: RegularExpression;
59
+ } | {
60
+ type: 'end-anchor';
61
+ left: RegularExpression;
62
+ right: RegularExpression;
63
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { type Parser } from './parser.js';
2
+ import { type RegularExpression } from './regularExpression.js';
3
+ export declare const regularExpressionParser: Parser<RegularExpression, string>;