@futpib/parser 1.0.2 → 1.0.4

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 (311) hide show
  1. package/.github/copilot-instructions.md +149 -0
  2. package/.github/workflows/copilot-setup-steps.yml +18 -0
  3. package/.github/workflows/main.yml +29 -8
  4. package/.yarn/releases/yarn-4.9.4.cjs +942 -0
  5. package/.yarnrc.yml +1 -1
  6. package/build/allSettledStream.js +1 -1
  7. package/build/allSettledStream.test.js +2 -2
  8. package/build/androidPackageParser.d.ts +1 -1
  9. package/build/androidPackageParser.js +5 -3
  10. package/build/androidPackageParser.test.js +7 -7
  11. package/build/androidPackageUnparser.d.ts +2 -2
  12. package/build/androidPackageUnparser.js +18 -14
  13. package/build/androidPackageUnparser.test.js +7 -7
  14. package/build/arbitrarilySlicedAsyncInterator.js +2 -1
  15. package/build/arbitraryDalvikBytecode.d.ts +4 -0
  16. package/build/arbitraryDalvikBytecode.js +640 -0
  17. package/build/arbitraryDalvikExecutable.d.ts +3 -0
  18. package/build/arbitraryDalvikExecutable.js +282 -0
  19. package/build/arbitraryDosDateTime.js +1 -0
  20. package/build/arbitraryZipStream.js +1 -1
  21. package/build/arrayParser.js +2 -2
  22. package/build/arrayUnparser.d.ts +1 -1
  23. package/build/backsmali.d.ts +3 -1
  24. package/build/backsmali.js +31 -3
  25. package/build/bash.d.ts +84 -0
  26. package/build/bash.js +1 -0
  27. package/build/bashParser.d.ts +6 -0
  28. package/build/bashParser.js +294 -0
  29. package/build/bashParser.test.d.ts +1 -0
  30. package/build/bashParser.test.js +181 -0
  31. package/build/customInvariant.d.ts +2 -1
  32. package/build/customInvariant.js +4 -6
  33. package/build/dalvikBytecodeParser/formatParsers.d.ts +76 -2
  34. package/build/dalvikBytecodeParser/formatParsers.js +146 -11
  35. package/build/dalvikBytecodeParser/formatSizes.d.ts +34 -0
  36. package/build/dalvikBytecodeParser/formatSizes.js +34 -0
  37. package/build/dalvikBytecodeParser/operationFormats.d.ts +225 -0
  38. package/build/dalvikBytecodeParser/operationFormats.js +225 -0
  39. package/build/dalvikBytecodeParser.d.ts +1105 -5
  40. package/build/dalvikBytecodeParser.js +658 -205
  41. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +152 -0
  42. package/build/dalvikBytecodeUnparser/formatUnparsers.js +225 -0
  43. package/build/dalvikBytecodeUnparser.d.ts +3 -0
  44. package/build/dalvikBytecodeUnparser.js +642 -0
  45. package/build/dalvikBytecodeUnparser.test.d.ts +1 -0
  46. package/build/dalvikBytecodeUnparser.test.js +25 -0
  47. package/build/dalvikExecutable.d.ts +65 -8
  48. package/build/dalvikExecutable.js +36 -0
  49. package/build/dalvikExecutableParser/stringSyntaxParser.d.ts +1 -1
  50. package/build/dalvikExecutableParser/stringSyntaxParser.js +17 -17
  51. package/build/dalvikExecutableParser/typeParsers.d.ts +2 -1
  52. package/build/dalvikExecutableParser/typeParsers.js +16 -11
  53. package/build/dalvikExecutableParser/typedNumbers.d.ts +85 -69
  54. package/build/dalvikExecutableParser/typedNumbers.js +0 -1
  55. package/build/dalvikExecutableParser.d.ts +2 -2
  56. package/build/dalvikExecutableParser.js +655 -337
  57. package/build/dalvikExecutableParser.test.js +24 -22
  58. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +223 -246
  59. package/build/dalvikExecutableUnparser/annotationUnparsers.d.ts +14 -0
  60. package/build/dalvikExecutableUnparser/annotationUnparsers.js +97 -0
  61. package/build/dalvikExecutableUnparser/poolBuilders.d.ts +49 -0
  62. package/build/dalvikExecutableUnparser/poolBuilders.js +140 -0
  63. package/build/dalvikExecutableUnparser/poolScanners.d.ts +4 -0
  64. package/build/dalvikExecutableUnparser/poolScanners.js +220 -0
  65. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +25 -0
  66. package/build/dalvikExecutableUnparser/sectionUnparsers.js +581 -0
  67. package/build/dalvikExecutableUnparser/utils.d.ts +10 -0
  68. package/build/dalvikExecutableUnparser/utils.js +108 -0
  69. package/build/dalvikExecutableUnparser.d.ts +4 -0
  70. package/build/dalvikExecutableUnparser.js +406 -0
  71. package/build/dalvikExecutableUnparser.test.d.ts +1 -0
  72. package/build/dalvikExecutableUnparser.test.js +31 -0
  73. package/build/debugLogInputParser.js +1 -1
  74. package/build/disjunctionParser.d.ts +2 -2
  75. package/build/disjunctionParser.js +2 -2
  76. package/build/elementTerminatedArrayParser.d.ts +2 -2
  77. package/build/elementTerminatedArrayParser.js +1 -1
  78. package/build/elementTerminatedArrayParser.test.js +5 -5
  79. package/build/elementTerminatedSequenceArrayParser.d.ts +2 -2
  80. package/build/elementTerminatedSequenceArrayParser.js +1 -1
  81. package/build/elementTerminatedSequenceArrayParser.test.js +2 -2
  82. package/build/elementTerminatedSequenceParser.d.ts +2 -2
  83. package/build/elementTerminatedSequenceParser.js +1 -1
  84. package/build/elementTerminatedSequenceParser.test.js +2 -2
  85. package/build/endOfInputParser.d.ts +1 -1
  86. package/build/exactElementSwitchParser.d.ts +3 -0
  87. package/build/exactElementSwitchParser.js +22 -0
  88. package/build/fetchCid.js +2 -6
  89. package/build/fetchCid.test.d.ts +1 -0
  90. package/build/fetchCid.test.js +16 -0
  91. package/build/fixedLengthSequenceParser.test.js +2 -2
  92. package/build/hasExecutable.js +2 -2
  93. package/build/highResolutionTimer.js +1 -1
  94. package/build/index.d.ts +24 -2
  95. package/build/index.js +22 -1
  96. package/build/inputReader.d.ts +1 -1
  97. package/build/inputReader.test.js +33 -45
  98. package/build/javaKeyStoreParser.test.js +6 -6
  99. package/build/jsonParser.js +8 -8
  100. package/build/lazyMessageError.d.ts +48 -0
  101. package/build/lazyMessageError.js +53 -0
  102. package/build/lazyMessageError.test.d.ts +1 -0
  103. package/build/lazyMessageError.test.js +15 -0
  104. package/build/leb128Parser.d.ts +1 -1
  105. package/build/leb128Parser.js +10 -10
  106. package/build/leb128Parser.test.js +7 -7
  107. package/build/negativeLookaheadParser.js +2 -2
  108. package/build/negativeLookaheadParser.test.js +4 -4
  109. package/build/noStackCaptureOverheadError.d.ts +4 -0
  110. package/build/noStackCaptureOverheadError.js +9 -0
  111. package/build/noStackCaptureOverheadError.test.d.ts +1 -0
  112. package/build/noStackCaptureOverheadError.test.js +15 -0
  113. package/build/nonEmptyArrayParser.js +2 -2
  114. package/build/nonEmptyArrayParser.test.js +2 -1
  115. package/build/optionalParser.js +2 -2
  116. package/build/parser.d.ts +2 -1
  117. package/build/parser.js +23 -8
  118. package/build/parser.test.js +78 -29
  119. package/build/parserConsumedSequenceParser.d.ts +1 -1
  120. package/build/parserConsumedSequenceParser.js +2 -2
  121. package/build/parserContext.d.ts +8 -6
  122. package/build/parserContext.js +60 -33
  123. package/build/parserContext.test.js +7 -3
  124. package/build/parserError.d.ts +603 -44
  125. package/build/parserError.js +98 -53
  126. package/build/parserImplementationInvariant.d.ts +1 -1
  127. package/build/parserImplementationInvariant.js +2 -2
  128. package/build/parserInputCompanion.js +2 -2
  129. package/build/promiseCompose.js +1 -2
  130. package/build/regexpParser.d.ts +2 -0
  131. package/build/regexpParser.js +71 -0
  132. package/build/regexpParser.test.d.ts +1 -0
  133. package/build/regexpParser.test.js +83 -0
  134. package/build/regularExpression.d.ts +63 -0
  135. package/build/regularExpression.js +1 -0
  136. package/build/regularExpressionParser.d.ts +3 -0
  137. package/build/regularExpressionParser.js +580 -0
  138. package/build/regularExpressionParser.test.d.ts +1 -0
  139. package/build/regularExpressionParser.test.js +89 -0
  140. package/build/separatedArrayParser.js +2 -2
  141. package/build/separatedNonEmptyArrayParser.d.ts +2 -0
  142. package/build/separatedNonEmptyArrayParser.js +40 -0
  143. package/build/separatedNonEmptyArrayParser.test.d.ts +1 -0
  144. package/build/separatedNonEmptyArrayParser.test.js +66 -0
  145. package/build/sequenceBuffer.js +1 -1
  146. package/build/sequenceTerminatedSequenceParser.d.ts +2 -2
  147. package/build/sequenceTerminatedSequenceParser.js +3 -3
  148. package/build/sequenceTerminatedSequenceParser.test.js +1 -1
  149. package/build/sequenceUnparser.d.ts +1 -1
  150. package/build/skipToParser.d.ts +1 -1
  151. package/build/skipToParser.js +2 -2
  152. package/build/sliceBoundedParser.test.js +4 -9
  153. package/build/smali.d.ts +1 -1
  154. package/build/smali.js +6 -2
  155. package/build/smaliParser.d.ts +62 -6
  156. package/build/smaliParser.js +1721 -296
  157. package/build/smaliParser.test.js +338 -43
  158. package/build/stringFromAsyncIterable.d.ts +1 -0
  159. package/build/stringFromAsyncIterable.js +7 -0
  160. package/build/terminatedArrayParser.js +4 -4
  161. package/build/terminatedArrayParser.test.js +7 -7
  162. package/build/toAsyncIterator.js +4 -4
  163. package/build/unionParser.d.ts +1 -1
  164. package/build/unionParser.js +2 -2
  165. package/build/unionParser.test.js +3 -3
  166. package/build/unparser.d.ts +3 -3
  167. package/build/unparser.js +6 -4
  168. package/build/unparser.test.js +7 -19
  169. package/build/unparserContext.d.ts +2 -2
  170. package/build/unparserContext.js +2 -3
  171. package/build/unparserError.d.ts +2 -1
  172. package/build/unparserError.js +2 -1
  173. package/build/unparserImplementationInvariant.d.ts +1 -1
  174. package/build/unparserOutputCompanion.d.ts +1 -1
  175. package/build/unparserOutputCompanion.js +1 -1
  176. package/build/zipParser.js +1 -1
  177. package/build/zipUnparser.d.ts +3 -3
  178. package/build/zipUnparser.js +9 -19
  179. package/build/zipUnparser.test.js +1 -1
  180. package/package.json +20 -26
  181. package/src/allSettledStream.test.ts +2 -2
  182. package/src/allSettledStream.ts +3 -3
  183. package/src/androidPackageParser.test.ts +17 -19
  184. package/src/androidPackageParser.ts +129 -171
  185. package/src/androidPackageUnparser.test.ts +19 -21
  186. package/src/androidPackageUnparser.ts +23 -17
  187. package/src/arbitrarilySlicedAsyncInterable.ts +1 -1
  188. package/src/arbitrarilySlicedAsyncInterator.ts +4 -4
  189. package/src/arbitraryDalvikBytecode.ts +992 -0
  190. package/src/arbitraryDalvikExecutable.ts +434 -0
  191. package/src/arbitraryDosDateTime.ts +1 -0
  192. package/src/arbitraryZipStream.ts +1 -1
  193. package/src/arrayParser.ts +2 -2
  194. package/src/arrayUnparser.ts +2 -2
  195. package/src/backsmali.ts +48 -4
  196. package/src/bash.ts +120 -0
  197. package/src/bashParser.test.ts +332 -0
  198. package/src/bashParser.ts +461 -0
  199. package/src/bsonParser.test.ts +12 -14
  200. package/src/customInvariant.ts +8 -12
  201. package/src/dalvikBytecodeParser/formatParsers.ts +376 -17
  202. package/src/dalvikBytecodeParser/formatSizes.ts +35 -0
  203. package/src/dalvikBytecodeParser/operationFormats.ts +226 -0
  204. package/src/dalvikBytecodeParser.ts +1042 -243
  205. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +442 -0
  206. package/src/dalvikBytecodeUnparser.test.ts +44 -0
  207. package/src/dalvikBytecodeUnparser.ts +758 -0
  208. package/src/dalvikExecutable.ts +110 -48
  209. package/src/dalvikExecutableParser/stringSyntaxParser.ts +33 -33
  210. package/src/dalvikExecutableParser/typeParsers.ts +23 -14
  211. package/src/dalvikExecutableParser/typedNumbers.ts +19 -19
  212. package/src/dalvikExecutableParser.test.ts +60 -60
  213. package/src/dalvikExecutableParser.test.ts.md +6 -6
  214. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  215. package/src/dalvikExecutableParser.ts +911 -434
  216. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +256 -239
  217. package/src/dalvikExecutableUnparser/annotationUnparsers.ts +135 -0
  218. package/src/dalvikExecutableUnparser/poolBuilders.ts +189 -0
  219. package/src/dalvikExecutableUnparser/poolScanners.ts +297 -0
  220. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +683 -0
  221. package/src/dalvikExecutableUnparser/utils.ts +149 -0
  222. package/src/dalvikExecutableUnparser.test.ts +57 -0
  223. package/src/dalvikExecutableUnparser.ts +581 -0
  224. package/src/debugLogInputParser.ts +1 -1
  225. package/src/disjunctionParser.ts +5 -5
  226. package/src/elementTerminatedArrayParser.test.ts +8 -8
  227. package/src/elementTerminatedArrayParser.ts +2 -2
  228. package/src/elementTerminatedSequenceArrayParser.test.ts +4 -6
  229. package/src/elementTerminatedSequenceArrayParser.ts +2 -2
  230. package/src/elementTerminatedSequenceParser.test.ts +4 -6
  231. package/src/elementTerminatedSequenceParser.ts +2 -2
  232. package/src/endOfInputParser.ts +1 -1
  233. package/src/exactElementSwitchParser.ts +41 -0
  234. package/src/fetchCid.test.ts +20 -0
  235. package/src/fetchCid.ts +3 -7
  236. package/src/fixedLengthSequenceParser.test.ts +10 -12
  237. package/src/hasExecutable.ts +2 -2
  238. package/src/highResolutionTimer.ts +1 -1
  239. package/src/index.ts +113 -2
  240. package/src/inputReader.test.ts +39 -52
  241. package/src/inputReader.ts +2 -4
  242. package/src/inputReaderState.ts +1 -1
  243. package/src/inspect.ts +1 -1
  244. package/src/javaKeyStoreParser.test.ts +12 -14
  245. package/src/javaKeyStoreParser.ts +2 -6
  246. package/src/jsonParser.test.ts +2 -4
  247. package/src/jsonParser.ts +34 -38
  248. package/src/lazyMessageError.test.ts +21 -0
  249. package/src/lazyMessageError.ts +88 -0
  250. package/src/leb128Parser.test.ts +25 -23
  251. package/src/leb128Parser.ts +19 -19
  252. package/src/negativeLookaheadParser.test.ts +7 -11
  253. package/src/negativeLookaheadParser.ts +2 -2
  254. package/src/noStackCaptureOverheadError.test.ts +17 -0
  255. package/src/noStackCaptureOverheadError.ts +12 -0
  256. package/src/nonEmptyArrayParser.test.ts +3 -2
  257. package/src/nonEmptyArrayParser.ts +2 -2
  258. package/src/optionalParser.ts +2 -2
  259. package/src/parser.test.ts +96 -43
  260. package/src/parser.test.ts.md +13 -6
  261. package/src/parser.test.ts.snap +0 -0
  262. package/src/parser.ts +35 -12
  263. package/src/parserAccessorParser.ts +1 -1
  264. package/src/parserConsumedSequenceParser.ts +3 -3
  265. package/src/parserContext.test.ts +7 -3
  266. package/src/parserContext.ts +82 -48
  267. package/src/parserError.ts +143 -63
  268. package/src/parserImplementationInvariant.ts +3 -3
  269. package/src/parserInputCompanion.ts +2 -2
  270. package/src/promiseCompose.ts +2 -2
  271. package/src/regexpParser.test.ts +186 -0
  272. package/src/regexpParser.ts +94 -0
  273. package/src/regularExpression.ts +24 -0
  274. package/src/regularExpressionParser.test.ts +102 -0
  275. package/src/regularExpressionParser.ts +921 -0
  276. package/src/separatedArrayParser.ts +3 -3
  277. package/src/separatedNonEmptyArrayParser.test.ts +117 -0
  278. package/src/separatedNonEmptyArrayParser.ts +61 -0
  279. package/src/sequenceBuffer.test.ts +9 -9
  280. package/src/sequenceBuffer.ts +4 -4
  281. package/src/sequenceTerminatedSequenceParser.test.ts +3 -5
  282. package/src/sequenceTerminatedSequenceParser.ts +4 -4
  283. package/src/sequenceUnparser.ts +2 -2
  284. package/src/skipToParser.ts +2 -2
  285. package/src/sliceBoundedParser.test.ts +4 -12
  286. package/src/sliceBoundedParser.ts +2 -2
  287. package/src/smali.ts +8 -3
  288. package/src/smaliParser.test.ts +377 -66
  289. package/src/smaliParser.test.ts.md +1635 -48
  290. package/src/smaliParser.test.ts.snap +0 -0
  291. package/src/smaliParser.ts +2751 -569
  292. package/src/stringFromAsyncIterable.ts +9 -0
  293. package/src/terminatedArrayParser.test.ts +11 -11
  294. package/src/terminatedArrayParser.ts +5 -7
  295. package/src/toAsyncIterator.ts +8 -8
  296. package/src/uint8Array.ts +2 -3
  297. package/src/unionParser.test.ts +22 -23
  298. package/src/unionParser.ts +6 -8
  299. package/src/unparser.test.ts +18 -34
  300. package/src/unparser.ts +13 -9
  301. package/src/unparserContext.ts +9 -13
  302. package/src/unparserError.ts +2 -1
  303. package/src/unparserImplementationInvariant.ts +1 -1
  304. package/src/unparserOutputCompanion.ts +1 -1
  305. package/src/zip.ts +2 -6
  306. package/src/zipParser.ts +10 -18
  307. package/src/zipUnparser.test.ts +1 -1
  308. package/src/zipUnparser.ts +52 -64
  309. package/tsconfig.json +7 -1
  310. package/xo.config.ts +15 -0
  311. package/.yarn/releases/yarn-4.5.3.cjs +0 -934
@@ -0,0 +1,186 @@
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
+
8
+ test('regexpParser matches digits', async t => {
9
+ const regexpParser = createRegExpParser(/\d+/);
10
+
11
+ const result = await runParser(
12
+ regexpParser,
13
+ '123',
14
+ stringParserInputCompanion,
15
+ );
16
+
17
+ t.is(result[0], '123');
18
+ });
19
+
20
+ test('regexpParser matches at start only', async t => {
21
+ const regexpParser = createRegExpParser(/\d+/);
22
+
23
+ const { output, remainingInput } = await runParserWithRemainingInput(
24
+ regexpParser,
25
+ '123abc',
26
+ stringParserInputCompanion,
27
+ );
28
+
29
+ t.is(output[0], '123');
30
+ t.truthy(remainingInput);
31
+ });
32
+
33
+ test('regexpParser fails when no match at start', async t => {
34
+ const regexpParser = createRegExpParser(/\d+/);
35
+
36
+ await t.throwsAsync(
37
+ runParser(
38
+ regexpParser,
39
+ 'abc123',
40
+ stringParserInputCompanion,
41
+ ),
42
+ );
43
+ });
44
+
45
+ test('regexpParser with capture groups', async t => {
46
+ const regexpParser = createRegExpParser(/(\d+)-(\d+)/);
47
+
48
+ const result = await runParser(
49
+ regexpParser,
50
+ '123-456',
51
+ stringParserInputCompanion,
52
+ );
53
+
54
+ t.is(result[0], '123-456');
55
+ t.is(result[1], '123');
56
+ t.is(result[2], '456');
57
+ });
58
+
59
+ test('regexpParser greedy matching', async t => {
60
+ const regexpParser = createRegExpParser(/a+/);
61
+
62
+ const { output } = await runParserWithRemainingInput(
63
+ regexpParser,
64
+ 'aaab',
65
+ stringParserInputCompanion,
66
+ );
67
+
68
+ t.is(output[0], 'aaa');
69
+ });
70
+
71
+ test('regexpParser with anchored regexp', async t => {
72
+ const regexpParser = createRegExpParser(/^hello/);
73
+
74
+ const { output } = await runParserWithRemainingInput(
75
+ regexpParser,
76
+ 'hello world',
77
+ stringParserInputCompanion,
78
+ );
79
+
80
+ t.is(output[0], 'hello');
81
+ });
82
+
83
+ testProp.serial(
84
+ 'regexpParser matches word characters',
85
+ [
86
+ fc.tuple(
87
+ fc.stringMatching(/^\w+$/),
88
+ fc.stringMatching(/^\W*$/),
89
+ ),
90
+ ],
91
+ async (t, [ word, nonWord ]) => {
92
+ const regexpParser = createRegExpParser(/\w+/);
93
+
94
+ const { output, position } = await runParserWithRemainingInput(
95
+ regexpParser,
96
+ word + nonWord,
97
+ stringParserInputCompanion,
98
+ );
99
+
100
+ t.is(output[0], word);
101
+ t.is(position, word.length);
102
+ },
103
+ {
104
+ verbose: true,
105
+ },
106
+ );
107
+
108
+ // Tests for zero-width/optional patterns at end of input
109
+
110
+ test('regexpParser with star quantifier on empty input', async t => {
111
+ const regexpParser = createRegExpParser(/a*/);
112
+
113
+ const result = await runParser(
114
+ regexpParser,
115
+ '',
116
+ stringParserInputCompanion,
117
+ );
118
+
119
+ t.is(result[0], '');
120
+ });
121
+
122
+ test('regexpParser with optional whitespace on empty input', async t => {
123
+ const regexpParser = createRegExpParser(/[ \t]*/);
124
+
125
+ const result = await runParser(
126
+ regexpParser,
127
+ '',
128
+ stringParserInputCompanion,
129
+ );
130
+
131
+ t.is(result[0], '');
132
+ });
133
+
134
+ test('regexpParser with star quantifier at end of input (no match)', async t => {
135
+ const regexpParser = createRegExpParser(/a*/);
136
+
137
+ const { output } = await runParserWithRemainingInput(
138
+ regexpParser,
139
+ 'bbb',
140
+ stringParserInputCompanion,
141
+ );
142
+
143
+ t.is(output[0], '');
144
+ });
145
+
146
+ test('regexpParser with optional group on empty input', async t => {
147
+ const regexpParser = createRegExpParser(/(?:foo)?/);
148
+
149
+ const result = await runParser(
150
+ regexpParser,
151
+ '',
152
+ stringParserInputCompanion,
153
+ );
154
+
155
+ t.is(result[0], '');
156
+ });
157
+
158
+ // Tests for negative lookahead
159
+
160
+ test('regexpParser with negative lookahead should not match when followed by same char', async t => {
161
+ // This regex should NOT match anything in '||' - the | is followed by another |
162
+ const regexpParser = createRegExpParser(/\|(?!\|)/);
163
+
164
+ await t.throwsAsync(
165
+ runParser(
166
+ regexpParser,
167
+ '||',
168
+ stringParserInputCompanion,
169
+ ),
170
+ );
171
+ });
172
+
173
+ test('regexpParser with negative lookahead should match single char', async t => {
174
+ // This regex should match single '|' when followed by something else
175
+ const regexpParser = createRegExpParser(/\|(?!\|)/);
176
+
177
+ const { output, position, remainingInput } = await runParserWithRemainingInput(
178
+ regexpParser,
179
+ '| ',
180
+ stringParserInputCompanion,
181
+ );
182
+
183
+ t.is(output[0], '|');
184
+ t.is(position, 1); // Consumed 1 character
185
+ t.truthy(remainingInput); // There's remaining input (the space)
186
+ });
@@ -0,0 +1,94 @@
1
+ import { type Parser, setParserName } from './parser.js';
2
+
3
+ export const createRegExpParser = (
4
+ regexp: RegExp,
5
+ ): Parser<RegExpExecArray, string, string> => {
6
+ const regexpParser: Parser<RegExpExecArray, string, string> = async parserContext => {
7
+ let start = 0;
8
+ let window = 1;
9
+ let lastMatch: RegExpExecArray | undefined;
10
+ let reachedEndOfInput = false;
11
+
12
+ while (true) {
13
+ const sequence = await parserContext.peekSequence(start, start + window);
14
+
15
+ if (sequence === undefined) {
16
+ reachedEndOfInput = true;
17
+ window = Math.floor(window / 2);
18
+
19
+ if (window === 0) {
20
+ // Get the full sequence we've accumulated to verify matches
21
+ const fullSequence = await parserContext.peekSequence(0, start);
22
+
23
+ // Verify any previous match is still valid with full context
24
+ // For lookahead/lookbehind assertions, additional input might invalidate a match
25
+ if (fullSequence !== undefined) {
26
+ const verifyMatch = regexp.exec(fullSequence);
27
+ if (verifyMatch !== null && verifyMatch.index === 0) {
28
+ parserContext.skip(verifyMatch[0].length);
29
+ return verifyMatch;
30
+ }
31
+ } else if (lastMatch !== undefined) {
32
+ // No full sequence available but we have a previous match
33
+ parserContext.skip(lastMatch[0].length);
34
+ return lastMatch;
35
+ }
36
+
37
+ // No previous match - try matching against empty string for zero-width patterns (e.g., /a*/, /[ \t]*/)
38
+ const emptyMatch = regexp.exec('');
39
+ if (emptyMatch !== null && emptyMatch.index === 0) {
40
+ return emptyMatch;
41
+ }
42
+
43
+ return parserContext.invariant(false, 'Unexpected end of input without regex match');
44
+ }
45
+
46
+ continue;
47
+ }
48
+
49
+ const fullSequence = await parserContext.peekSequence(0, start + window);
50
+
51
+ if (fullSequence === undefined) {
52
+ continue;
53
+ }
54
+
55
+ const match = regexp.exec(fullSequence);
56
+
57
+ if (match === null || match.index !== 0) {
58
+ if (lastMatch !== undefined) {
59
+ // Verify lastMatch is still valid with current full context
60
+ // For lookahead/lookbehind assertions, a match on shorter input might be
61
+ // invalidated by additional input (e.g., /\|(?!\|)/ matches '|' but not '||')
62
+ const verifyMatch = regexp.exec(fullSequence);
63
+ if (verifyMatch !== null && verifyMatch.index === 0) {
64
+ parserContext.skip(verifyMatch[0].length);
65
+ return verifyMatch;
66
+ }
67
+ // lastMatch was invalidated by additional context
68
+ lastMatch = undefined;
69
+ }
70
+
71
+ if (reachedEndOfInput) {
72
+ parserContext.invariant(
73
+ false,
74
+ 'Regex did not match at start of input',
75
+ );
76
+ }
77
+
78
+ start += window;
79
+ window *= 2;
80
+
81
+ continue;
82
+ }
83
+
84
+ lastMatch = match;
85
+
86
+ start += window;
87
+ window *= 2;
88
+ }
89
+ };
90
+
91
+ setParserName(regexpParser, regexp.toString());
92
+
93
+ return regexpParser;
94
+ };
@@ -0,0 +1,24 @@
1
+ export type CodePointRange = {
2
+ start: number;
3
+ end: number;
4
+ };
5
+
6
+ export type CharacterSet =
7
+ | { type: 'empty' }
8
+ | { type: 'node'; range: CodePointRange; left: CharacterSet; right: CharacterSet };
9
+
10
+ export type RepeatBounds = number | { min: number; max?: number } | { min?: number; max: number };
11
+
12
+ export type RegularExpression =
13
+ | { type: 'epsilon' }
14
+ | { type: 'literal'; charset: CharacterSet }
15
+ | { type: 'concat'; left: RegularExpression; right: RegularExpression }
16
+ | { type: 'union'; left: RegularExpression; right: RegularExpression }
17
+ | { type: 'star'; inner: RegularExpression }
18
+ | { type: 'plus'; inner: RegularExpression }
19
+ | { type: 'optional'; inner: RegularExpression }
20
+ | { type: 'repeat'; inner: RegularExpression; bounds: RepeatBounds }
21
+ | { type: 'capture-group'; inner: RegularExpression; name?: string }
22
+ | { type: 'lookahead'; isPositive: boolean; inner: RegularExpression; right: RegularExpression }
23
+ | { type: 'start-anchor'; left: RegularExpression; right: RegularExpression }
24
+ | { type: 'end-anchor'; left: RegularExpression; right: RegularExpression };
@@ -0,0 +1,102 @@
1
+ import { testProp, fc } from '@fast-check/ava';
2
+ import { regularExpressionParser } from './regularExpressionParser.js';
3
+
4
+ const seed = process.env.SEED ? Number(process.env.SEED) : undefined;
5
+
6
+ // Import directly from file path to bypass package exports
7
+ // eslint-disable-next-line import/no-unresolved
8
+ import { parseRegExpString } from '../node_modules/@gruhn/regex-utils/dist/regex-parser.js';
9
+ import { runParser } from './parser.js';
10
+ import { stringParserInputCompanion } from './parserInputCompanion.js';
11
+ import { arbitrarilySlicedAsyncIterator } from './arbitrarilySlicedAsyncInterator.js';
12
+ import type { RegularExpression, CharacterSet } from './regularExpression.js';
13
+
14
+ // Normalize AST for comparison - removes hashes from CharSets and normalizes structure
15
+ function normalizeCharacterSet(charset: CharacterSet): CharacterSet {
16
+ if (charset.type === 'empty') {
17
+ return { type: 'empty' };
18
+ }
19
+ return {
20
+ type: 'node',
21
+ range: { start: charset.range.start, end: charset.range.end },
22
+ left: normalizeCharacterSet(charset.left),
23
+ right: normalizeCharacterSet(charset.right),
24
+ };
25
+ }
26
+
27
+ function normalizeRegularExpression(ast: RegularExpression): RegularExpression {
28
+ switch (ast.type) {
29
+ case 'epsilon':
30
+ return { type: 'epsilon' };
31
+ case 'literal':
32
+ return { type: 'literal', charset: normalizeCharacterSet(ast.charset) };
33
+ case 'concat':
34
+ return { type: 'concat', left: normalizeRegularExpression(ast.left), right: normalizeRegularExpression(ast.right) };
35
+ case 'union':
36
+ return { type: 'union', left: normalizeRegularExpression(ast.left), right: normalizeRegularExpression(ast.right) };
37
+ case 'star':
38
+ return { type: 'star', inner: normalizeRegularExpression(ast.inner) };
39
+ case 'plus':
40
+ return { type: 'plus', inner: normalizeRegularExpression(ast.inner) };
41
+ case 'optional':
42
+ return { type: 'optional', inner: normalizeRegularExpression(ast.inner) };
43
+ case 'repeat':
44
+ return { type: 'repeat', inner: normalizeRegularExpression(ast.inner), bounds: ast.bounds };
45
+ case 'capture-group':
46
+ if (ast.name !== undefined) {
47
+ return { type: 'capture-group', inner: normalizeRegularExpression(ast.inner), name: ast.name };
48
+ }
49
+ return { type: 'capture-group', inner: normalizeRegularExpression(ast.inner) };
50
+ case 'lookahead':
51
+ return { type: 'lookahead', isPositive: ast.isPositive, inner: normalizeRegularExpression(ast.inner), right: normalizeRegularExpression(ast.right) };
52
+ case 'start-anchor':
53
+ return { type: 'start-anchor', left: normalizeRegularExpression(ast.left), right: normalizeRegularExpression(ast.right) };
54
+ case 'end-anchor':
55
+ return { type: 'end-anchor', left: normalizeRegularExpression(ast.left), right: normalizeRegularExpression(ast.right) };
56
+ }
57
+ }
58
+
59
+ // Generate regex patterns that are likely to be supported
60
+ const supportedRegexArbitrary = fc.stringMatching(
61
+ /^([a-zA-Z0-9]|\\[dDwWsS.]|\[(\^)?([a-zA-Z0-9](-[a-zA-Z0-9])?|\\[dDwWsS])*\]|\.|\((\?[:=!])?[a-zA-Z0-9]*\)|[*+?]|\{[0-9]+(,[0-9]*)?\}|\||\^|\$)*$/,
62
+ ).filter(s => {
63
+ // Filter out patterns that JavaScript doesn't support
64
+ try {
65
+ new RegExp(s);
66
+ } catch {
67
+ return false;
68
+ }
69
+ // Filter out patterns that @gruhn/regex-utils doesn't support
70
+ try {
71
+ parseRegExpString(s);
72
+ } catch {
73
+ return false;
74
+ }
75
+ // Filter out quantified lookaheads - @gruhn/regex-utils has a bug where it treats
76
+ // quantifiers after lookaheads as literals instead of quantifiers.
77
+ // See: https://github.com/gruhn/regex-utils/issues/13
78
+ // JavaScript allows (?=a){2} but @gruhn/regex-utils parses {2} as literal text.
79
+ if (/\(\?[=!][^)]*\)[*+?]|\(\?[=!][^)]*\)\{[0-9]/.test(s)) {
80
+ return false;
81
+ }
82
+ return true;
83
+ });
84
+
85
+ testProp(
86
+ 'regularExpressionParser matches @gruhn/regex-utils',
87
+ [
88
+ arbitrarilySlicedAsyncIterator(supportedRegexArbitrary),
89
+ ],
90
+ async (t, [regexStr, regexStringChunkIterator]) => {
91
+ const expected = normalizeRegularExpression(parseRegExpString(regexStr));
92
+ const actual = normalizeRegularExpression(await runParser(regularExpressionParser, regexStringChunkIterator, stringParserInputCompanion, {
93
+ errorJoinMode: 'none',
94
+ }));
95
+
96
+ t.deepEqual(actual, expected);
97
+ },
98
+ {
99
+ verbose: true,
100
+ seed,
101
+ },
102
+ );