@futpib/parser 1.0.4 → 1.0.7

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 (253) 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 +6 -1
  20. package/build/bashParser.js +414 -131
  21. package/build/bashParser.test.js +233 -0
  22. package/build/bashParserEdgeCases.test.d.ts +1 -0
  23. package/build/bashParserEdgeCases.test.js +117 -0
  24. package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
  25. package/build/dalvikBytecodeParser/addressConversion.js +334 -0
  26. package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
  27. package/build/dalvikBytecodeParser/formatParsers.js +13 -14
  28. package/build/dalvikBytecodeParser.d.ts +60 -31
  29. package/build/dalvikBytecodeParser.js +92 -35
  30. package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
  31. package/build/dalvikBytecodeParser.test-d.js +268 -0
  32. package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
  33. package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
  34. package/build/dalvikBytecodeUnparser.d.ts +2 -2
  35. package/build/dalvikBytecodeUnparser.js +23 -23
  36. package/build/dalvikBytecodeUnparser.test.js +7 -7
  37. package/build/dalvikExecutable.d.ts +3 -3
  38. package/build/dalvikExecutable.test-d.d.ts +1 -0
  39. package/build/dalvikExecutable.test-d.js +59 -0
  40. package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
  41. package/build/dalvikExecutableParser/typedNumbers.js +3 -0
  42. package/build/dalvikExecutableParser.d.ts +2 -1
  43. package/build/dalvikExecutableParser.js +96 -77
  44. package/build/dalvikExecutableParser.test.js +24 -3
  45. package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
  46. package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
  47. package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
  48. package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
  49. package/build/dalvikExecutableUnparser.d.ts +2 -2
  50. package/build/dalvikExecutableUnparser.test.js +2 -1
  51. package/build/disjunctionParser.d.ts +5 -3
  52. package/build/disjunctionParser.js +79 -17
  53. package/build/disjunctionParser.test-d.d.ts +1 -0
  54. package/build/disjunctionParser.test-d.js +72 -0
  55. package/build/elementSwitchParser.d.ts +4 -0
  56. package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
  57. package/build/elementSwitchParser.test-d.d.ts +1 -0
  58. package/build/elementSwitchParser.test-d.js +44 -0
  59. package/build/exactSequenceParser.d.ts +4 -2
  60. package/build/exactSequenceParser.test-d.d.ts +1 -0
  61. package/build/exactSequenceParser.test-d.js +36 -0
  62. package/build/fetchCid.js +2 -66
  63. package/build/index.d.ts +4 -2
  64. package/build/index.js +3 -1
  65. package/build/index.test.js +16 -1
  66. package/build/inputReader.d.ts +10 -0
  67. package/build/inputReader.js +36 -0
  68. package/build/java.d.ts +502 -0
  69. package/build/java.js +2 -0
  70. package/build/javaKeyStoreParser.js +14 -17
  71. package/build/javaParser.d.ts +51 -0
  72. package/build/javaParser.js +1538 -0
  73. package/build/javaParser.test.d.ts +1 -0
  74. package/build/javaParser.test.js +1287 -0
  75. package/build/javaScript.d.ts +35 -0
  76. package/build/javaScript.js +1 -0
  77. package/build/javaScriptParser.d.ts +9 -0
  78. package/build/javaScriptParser.js +34 -0
  79. package/build/javaScriptUnparser.d.ts +3 -0
  80. package/build/javaScriptUnparser.js +4 -0
  81. package/build/javaScriptUnparser.test.d.ts +1 -0
  82. package/build/javaScriptUnparser.test.js +24 -0
  83. package/build/javaUnparser.d.ts +2 -0
  84. package/build/javaUnparser.js +519 -0
  85. package/build/javaUnparser.test.d.ts +1 -0
  86. package/build/javaUnparser.test.js +24 -0
  87. package/build/javascript.d.ts +35 -0
  88. package/build/javascript.js +1 -0
  89. package/build/javascriptParser.d.ts +9 -0
  90. package/build/javascriptParser.js +34 -0
  91. package/build/javascriptUnparser.d.ts +3 -0
  92. package/build/javascriptUnparser.js +4 -0
  93. package/build/javascriptUnparser.test.d.ts +1 -0
  94. package/build/javascriptUnparser.test.js +24 -0
  95. package/build/jsonParser.js +2 -12
  96. package/build/lazyMessageError.d.ts +3 -0
  97. package/build/lookaheadParser.js +60 -3
  98. package/build/negativeLookaheadParser.js +70 -11
  99. package/build/nonEmptyArrayParser.js +72 -13
  100. package/build/objectParser.d.ts +12 -0
  101. package/build/objectParser.js +31 -0
  102. package/build/objectParser.test-d.d.ts +1 -0
  103. package/build/objectParser.test-d.js +112 -0
  104. package/build/objectParser.test.d.ts +1 -0
  105. package/build/objectParser.test.js +55 -0
  106. package/build/optionalParser.js +69 -10
  107. package/build/parser.d.ts +4 -0
  108. package/build/parser.js +3 -1
  109. package/build/parser.test.js +114 -1
  110. package/build/parserConsumedSequenceParser.js +66 -7
  111. package/build/parserContext.d.ts +6 -0
  112. package/build/parserContext.js +20 -11
  113. package/build/parserError.d.ts +119 -27
  114. package/build/parserError.js +16 -8
  115. package/build/predicateElementParser.d.ts +3 -0
  116. package/build/predicateElementParser.js +10 -0
  117. package/build/regexpParser.js +33 -3
  118. package/build/regexpParser.test.js +31 -0
  119. package/build/regularExpressionParser.js +35 -15
  120. package/build/separatedArrayParser.js +73 -14
  121. package/build/separatedNonEmptyArrayParser.js +73 -14
  122. package/build/sliceBoundedParser.js +62 -5
  123. package/build/smaliParser.d.ts +7 -7
  124. package/build/smaliParser.js +185 -268
  125. package/build/smaliParser.test.js +58 -0
  126. package/build/stringEscapes.d.ts +5 -0
  127. package/build/stringEscapes.js +244 -0
  128. package/build/symbolicExpression.d.ts +29 -0
  129. package/build/symbolicExpression.js +1 -0
  130. package/build/symbolicExpressionParser.d.ts +4 -0
  131. package/build/symbolicExpressionParser.js +123 -0
  132. package/build/symbolicExpressionParser.test.d.ts +1 -0
  133. package/build/symbolicExpressionParser.test.js +289 -0
  134. package/build/terminatedArrayParser.js +113 -38
  135. package/build/terminatedArrayParser.test.js +4 -2
  136. package/build/tupleParser.d.ts +7 -15
  137. package/build/tupleParser.js +1 -0
  138. package/build/unionParser.d.ts +5 -3
  139. package/build/unionParser.js +7 -2
  140. package/build/unionParser.test-d.d.ts +1 -0
  141. package/build/unionParser.test-d.js +72 -0
  142. package/build/unionParser.test.js +10 -11
  143. package/build/zig.d.ts +280 -0
  144. package/build/zig.js +2 -0
  145. package/build/zigParser.d.ts +3 -0
  146. package/build/zigParser.js +1119 -0
  147. package/build/zigParser.test.d.ts +1 -0
  148. package/build/zigParser.test.js +1590 -0
  149. package/build/zigUnparser.d.ts +2 -0
  150. package/build/zigUnparser.js +460 -0
  151. package/build/zigUnparser.test.d.ts +1 -0
  152. package/build/zigUnparser.test.js +24 -0
  153. package/build/zipParser.js +19 -32
  154. package/build/zipUnparser.js +19 -7
  155. package/build/zipUnparser.test.js +1 -1
  156. package/node_modules-@types/s-expression/index.d.ts +5 -0
  157. package/package.json +24 -6
  158. package/src/androidPackageParser.ts +33 -60
  159. package/src/arbitraryDalvikBytecode.ts +39 -31
  160. package/src/arbitraryDalvikExecutable.ts +65 -20
  161. package/src/arbitraryJava.ts +804 -0
  162. package/src/arbitraryJavaScript.ts +410 -0
  163. package/src/arbitraryZig.ts +380 -0
  164. package/src/arrayParser.ts +1 -3
  165. package/src/backsmali.ts +35 -4
  166. package/src/bash.ts +8 -1
  167. package/src/bashParser.test.ts +396 -0
  168. package/src/bashParser.ts +564 -199
  169. package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
  170. package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
  171. package/src/dalvikBytecodeParser.test-d.ts +310 -0
  172. package/src/dalvikBytecodeParser.ts +194 -69
  173. package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
  174. package/src/dalvikBytecodeUnparser.test.ts +7 -7
  175. package/src/dalvikBytecodeUnparser.ts +31 -30
  176. package/src/dalvikExecutable.test-d.ts +132 -0
  177. package/src/dalvikExecutable.ts +3 -3
  178. package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
  179. package/src/dalvikExecutableParser.test.ts +37 -3
  180. package/src/dalvikExecutableParser.test.ts.md +163 -2
  181. package/src/dalvikExecutableParser.test.ts.snap +0 -0
  182. package/src/dalvikExecutableParser.ts +121 -139
  183. package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
  184. package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
  185. package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
  186. package/src/dalvikExecutableUnparser.test.ts +3 -2
  187. package/src/dalvikExecutableUnparser.ts +4 -4
  188. package/src/disjunctionParser.test-d.ts +105 -0
  189. package/src/disjunctionParser.ts +18 -15
  190. package/src/elementSwitchParser.test-d.ts +74 -0
  191. package/src/elementSwitchParser.ts +51 -0
  192. package/src/exactSequenceParser.test-d.ts +43 -0
  193. package/src/exactSequenceParser.ts +13 -8
  194. package/src/fetchCid.ts +2 -76
  195. package/src/index.test.ts +22 -1
  196. package/src/index.ts +11 -1
  197. package/src/inputReader.ts +53 -0
  198. package/src/java.ts +708 -0
  199. package/src/javaKeyStoreParser.ts +18 -32
  200. package/src/javaParser.test.ts +1592 -0
  201. package/src/javaParser.ts +2640 -0
  202. package/src/javaScript.ts +36 -0
  203. package/src/javaScriptParser.ts +57 -0
  204. package/src/javaScriptUnparser.test.ts +37 -0
  205. package/src/javaScriptUnparser.ts +7 -0
  206. package/src/javaUnparser.test.ts +37 -0
  207. package/src/javaUnparser.ts +640 -0
  208. package/src/jsonParser.ts +6 -27
  209. package/src/lookaheadParser.ts +2 -6
  210. package/src/negativeLookaheadParser.ts +1 -3
  211. package/src/nonEmptyArrayParser.ts +1 -3
  212. package/src/objectParser.test-d.ts +152 -0
  213. package/src/objectParser.test.ts +71 -0
  214. package/src/objectParser.ts +69 -0
  215. package/src/optionalParser.ts +1 -3
  216. package/src/parser.test.ts +151 -4
  217. package/src/parser.ts +11 -1
  218. package/src/parserConsumedSequenceParser.ts +2 -4
  219. package/src/parserContext.ts +26 -11
  220. package/src/parserError.ts +17 -3
  221. package/src/predicateElementParser.ts +22 -0
  222. package/src/regexpParser.test.ts +78 -0
  223. package/src/regexpParser.ts +35 -3
  224. package/src/regularExpressionParser.ts +36 -37
  225. package/src/separatedArrayParser.ts +1 -3
  226. package/src/separatedNonEmptyArrayParser.ts +1 -3
  227. package/src/sliceBoundedParser.test.ts +2 -2
  228. package/src/sliceBoundedParser.ts +15 -19
  229. package/src/smaliParser.test.ts +64 -0
  230. package/src/smaliParser.test.ts.md +12 -12
  231. package/src/smaliParser.test.ts.snap +0 -0
  232. package/src/smaliParser.ts +246 -534
  233. package/src/stringEscapes.ts +253 -0
  234. package/src/symbolicExpression.ts +17 -0
  235. package/src/symbolicExpressionParser.test.ts +466 -0
  236. package/src/symbolicExpressionParser.ts +190 -0
  237. package/src/terminatedArrayParser.test.ts +9 -6
  238. package/src/terminatedArrayParser.ts +25 -29
  239. package/src/tupleParser.ts +21 -18
  240. package/src/unionParser.test-d.ts +105 -0
  241. package/src/unionParser.test.ts +18 -17
  242. package/src/unionParser.ts +28 -16
  243. package/src/zig.ts +411 -0
  244. package/src/zigParser.test.ts +1693 -0
  245. package/src/zigParser.ts +1745 -0
  246. package/src/zigUnparser.test.ts +37 -0
  247. package/src/zigUnparser.ts +615 -0
  248. package/src/zipParser.ts +20 -56
  249. package/src/zipUnparser.test.ts +1 -1
  250. package/src/zipUnparser.ts +22 -7
  251. package/tsconfig.json +2 -2
  252. package/build/exactElementSwitchParser.d.ts +0 -3
  253. package/src/exactElementSwitchParser.ts +0 -41
@@ -98,6 +98,55 @@ test('double quoted string with variable', async (t) => {
98
98
  });
99
99
  }
100
100
  });
101
+ test('double quoted string with trailing dollar', async (t) => {
102
+ const result = await runParser(bashScriptParser, 'echo "hello$"', stringParserInputCompanion);
103
+ const cmd = result.entries[0].pipeline.commands[0];
104
+ if (cmd.type === 'simple') {
105
+ t.deepEqual(cmd.args[0], {
106
+ parts: [{
107
+ type: 'doubleQuoted',
108
+ parts: [
109
+ { type: 'literal', value: 'hello' },
110
+ { type: 'literal', value: '$' },
111
+ ],
112
+ }],
113
+ });
114
+ }
115
+ });
116
+ test('double quoted string with only dollar', async (t) => {
117
+ const result = await runParser(bashScriptParser, 'echo "$"', stringParserInputCompanion);
118
+ const cmd = result.entries[0].pipeline.commands[0];
119
+ if (cmd.type === 'simple') {
120
+ t.deepEqual(cmd.args[0], {
121
+ parts: [{
122
+ type: 'doubleQuoted',
123
+ parts: [
124
+ { type: 'literal', value: '$' },
125
+ ],
126
+ }],
127
+ });
128
+ }
129
+ });
130
+ test('grep with dollar anchor in double quotes', async (t) => {
131
+ const result = await runParser(bashScriptParser, 'grep "\\.ts$"', stringParserInputCompanion);
132
+ const cmd = result.entries[0].pipeline.commands[0];
133
+ if (cmd.type === 'simple') {
134
+ t.deepEqual(cmd.args[0], {
135
+ parts: [{
136
+ type: 'doubleQuoted',
137
+ parts: [
138
+ { type: 'literal', value: '\\' },
139
+ { type: 'literal', value: '.ts' },
140
+ { type: 'literal', value: '$' },
141
+ ],
142
+ }],
143
+ });
144
+ }
145
+ });
146
+ test('pipeline with dollar anchor in double quoted grep pattern', async (t) => {
147
+ const result = await runParser(bashScriptParser, 'ls -la /home | grep "\\.ts$" | grep -v "\\.test\\.ts"', stringParserInputCompanion);
148
+ t.is(result.entries[0].pipeline.commands.length, 3);
149
+ });
101
150
  test('simple variable', async (t) => {
102
151
  const result = await runParser(bashScriptParser, 'echo $HOME', stringParserInputCompanion);
103
152
  const cmd = result.entries[0].pipeline.commands[0];
@@ -172,6 +221,119 @@ test('[[ treated as command name', async (t) => {
172
221
  t.is(cmd.args.length, 3); // -f, file, ]]
173
222
  }
174
223
  });
224
+ // Braced variable expansion: ${VAR}
225
+ test('braced variable expansion', async (t) => {
226
+ const result = await runParser(bashScriptParser, 'echo ${HOME}', stringParserInputCompanion);
227
+ const cmd = result.entries[0].pipeline.commands[0];
228
+ if (cmd.type === 'simple') {
229
+ t.is(cmd.args[0].parts[0].type, 'variableBraced');
230
+ }
231
+ });
232
+ // Braced variable with default: ${VAR:-default}
233
+ test('braced variable with default value', async (t) => {
234
+ const result = await runParser(bashScriptParser, 'echo ${VAR:-default}', stringParserInputCompanion);
235
+ const cmd = result.entries[0].pipeline.commands[0];
236
+ if (cmd.type === 'simple') {
237
+ t.is(cmd.args[0].parts[0].type, 'variableBraced');
238
+ }
239
+ });
240
+ // Arithmetic expansion: $((1+2))
241
+ test('arithmetic expansion', async (t) => {
242
+ const result = await runParser(bashScriptParser, 'echo $((1+2))', stringParserInputCompanion);
243
+ const cmd = result.entries[0].pipeline.commands[0];
244
+ if (cmd.type === 'simple') {
245
+ t.is(cmd.args[0].parts[0].type, 'arithmeticExpansion');
246
+ }
247
+ });
248
+ // Bare $ at end of unquoted word
249
+ test('bare dollar at end of unquoted word', async (t) => {
250
+ const result = await runParser(bashScriptParser, 'echo foo$', stringParserInputCompanion);
251
+ const cmd = result.entries[0].pipeline.commands[0];
252
+ if (cmd.type === 'simple') {
253
+ t.deepEqual(cmd.args[0], {
254
+ parts: [
255
+ { type: 'literal', value: 'foo' },
256
+ { type: 'literal', value: '$' },
257
+ ],
258
+ });
259
+ }
260
+ });
261
+ // Bare $ as its own unquoted word
262
+ test('bare dollar as standalone unquoted word', async (t) => {
263
+ const result = await runParser(bashScriptParser, 'echo $', stringParserInputCompanion);
264
+ const cmd = result.entries[0].pipeline.commands[0];
265
+ if (cmd.type === 'simple') {
266
+ t.deepEqual(cmd.args[0], {
267
+ parts: [
268
+ { type: 'literal', value: '$' },
269
+ ],
270
+ });
271
+ }
272
+ });
273
+ // Comment after command
274
+ test('comment after command', async (t) => {
275
+ const result = await runParser(bashScriptParser, 'echo hello # this is a comment', stringParserInputCompanion);
276
+ const cmd = result.entries[0].pipeline.commands[0];
277
+ if (cmd.type === 'simple') {
278
+ t.is(cmd.args.length, 1);
279
+ t.deepEqual(cmd.args[0], {
280
+ parts: [{ type: 'literal', value: 'hello' }],
281
+ });
282
+ }
283
+ });
284
+ // ANSI-C quoting: $'...'
285
+ test('ansi-c quoting', async (t) => {
286
+ const result = await runParser(bashScriptParser, "echo $'hello\\nworld'", stringParserInputCompanion);
287
+ const cmd = result.entries[0].pipeline.commands[0];
288
+ if (cmd.type === 'simple') {
289
+ t.is(cmd.args.length, 1);
290
+ }
291
+ });
292
+ // Braced variable in double quotes: "${VAR}"
293
+ test('braced variable in double quotes', async (t) => {
294
+ const result = await runParser(bashScriptParser, 'echo "${HOME}"', stringParserInputCompanion);
295
+ const cmd = result.entries[0].pipeline.commands[0];
296
+ if (cmd.type === 'simple') {
297
+ const dq = cmd.args[0].parts[0];
298
+ if (dq.type === 'doubleQuoted') {
299
+ t.is(dq.parts[0].type, 'variableBraced');
300
+ }
301
+ }
302
+ });
303
+ // Arithmetic expansion in double quotes
304
+ test('arithmetic expansion in double quotes', async (t) => {
305
+ const result = await runParser(bashScriptParser, 'echo "$((1+2))"', stringParserInputCompanion);
306
+ const cmd = result.entries[0].pipeline.commands[0];
307
+ if (cmd.type === 'simple') {
308
+ const dq = cmd.args[0].parts[0];
309
+ if (dq.type === 'doubleQuoted') {
310
+ t.is(dq.parts[0].type, 'arithmeticExpansion');
311
+ }
312
+ }
313
+ });
314
+ // Process substitution: <(cmd)
315
+ test('process substitution input', async (t) => {
316
+ const result = await runParser(bashScriptParser, 'diff <(sort file1) <(sort file2)', stringParserInputCompanion);
317
+ t.truthy(result);
318
+ });
319
+ // Line continuation (backslash-newline)
320
+ test('line continuation', async (t) => {
321
+ const result = await runParser(bashScriptParser, 'echo hello \\\nworld', stringParserInputCompanion);
322
+ const cmd = result.entries[0].pipeline.commands[0];
323
+ if (cmd.type === 'simple') {
324
+ t.is(cmd.args.length, 2);
325
+ }
326
+ });
327
+ // Hash in middle of unquoted word is literal, not a comment
328
+ test('hash in middle of unquoted word', async (t) => {
329
+ const result = await runParser(bashScriptParser, 'echo foo#bar', stringParserInputCompanion);
330
+ const cmd = result.entries[0].pipeline.commands[0];
331
+ if (cmd.type === 'simple') {
332
+ t.deepEqual(cmd.args[0], {
333
+ parts: [{ type: 'literal', value: 'foo#bar' }],
334
+ });
335
+ }
336
+ });
175
337
  test('if treated as command name', async (t) => {
176
338
  const result = await runParser(bashScriptParser, 'if true', stringParserInputCompanion);
177
339
  const cmd = result.entries[0].pipeline.commands[0];
@@ -179,3 +341,74 @@ test('if treated as command name', async (t) => {
179
341
  t.deepEqual(cmd.name, { parts: [{ type: 'literal', value: 'if' }] });
180
342
  }
181
343
  });
344
+ test('find -exec with {} placeholder', async (t) => {
345
+ const result = await runParser(bashScriptParser, 'find . -name "*.tmp" -exec rm {} \\;', stringParserInputCompanion);
346
+ const cmd = result.entries[0].pipeline.commands[0];
347
+ t.is(cmd.name.parts[0].type, 'literal');
348
+ t.is(cmd.name.parts[0].value, 'find');
349
+ // {} should be parsed as a literal word argument
350
+ const braceArg = cmd.args[5]; // ., -name, "*.tmp", -exec, rm, {}, \;
351
+ t.is(braceArg.parts[0].type, 'literal');
352
+ t.is(braceArg.parts[0].value, '{}');
353
+ });
354
+ test('lone open brace as argument', async (t) => {
355
+ const result = await runParser(bashScriptParser, 'echo {', stringParserInputCompanion);
356
+ const cmd = result.entries[0].pipeline.commands[0];
357
+ t.deepEqual(cmd.args[0], {
358
+ parts: [{ type: 'literal', value: '{' }],
359
+ });
360
+ });
361
+ test('close brace mid-word', async (t) => {
362
+ const result = await runParser(bashScriptParser, 'echo foo}bar', stringParserInputCompanion);
363
+ const cmd = result.entries[0].pipeline.commands[0];
364
+ t.is(cmd.args.length, 1);
365
+ t.is(cmd.args[0].parts[0].type, 'literal');
366
+ });
367
+ test('open brace mid-word', async (t) => {
368
+ const result = await runParser(bashScriptParser, 'echo foo{bar', stringParserInputCompanion);
369
+ const cmd = result.entries[0].pipeline.commands[0];
370
+ t.is(cmd.args.length, 1);
371
+ t.is(cmd.args[0].parts[0].type, 'literal');
372
+ });
373
+ test('braces mid-word like brace expansion', async (t) => {
374
+ const result = await runParser(bashScriptParser, 'echo file.{c,h}', stringParserInputCompanion);
375
+ const cmd = result.entries[0].pipeline.commands[0];
376
+ t.is(cmd.args.length, 1);
377
+ t.is(cmd.args[0].parts[0].type, 'literal');
378
+ });
379
+ test('find -exec with {.} placeholder variant', async (t) => {
380
+ const result = await runParser(bashScriptParser, 'echo {.}', stringParserInputCompanion);
381
+ const cmd = result.entries[0].pipeline.commands[0];
382
+ t.is(cmd.args.length, 1);
383
+ t.is(cmd.args[0].parts[0].type, 'literal');
384
+ });
385
+ test('lone close brace as argument', async (t) => {
386
+ const result = await runParser(bashScriptParser, 'echo }', stringParserInputCompanion);
387
+ const cmd = result.entries[0].pipeline.commands[0];
388
+ t.deepEqual(cmd.args[0], {
389
+ parts: [{ type: 'literal', value: '}' }],
390
+ });
391
+ });
392
+ test('close brace at start of word', async (t) => {
393
+ const result = await runParser(bashScriptParser, 'echo }hello', stringParserInputCompanion);
394
+ const cmd = result.entries[0].pipeline.commands[0];
395
+ t.is(cmd.args.length, 1);
396
+ t.is(cmd.args[0].parts[0].type, 'literal');
397
+ });
398
+ test('multi-line script with blank lines', async (t) => {
399
+ const result = await runParser(bashScriptParser, 'echo hello\n\necho world', stringParserInputCompanion);
400
+ t.is(result.entries.length, 2);
401
+ });
402
+ test('mid-script comment', async (t) => {
403
+ const result = await runParser(bashScriptParser, 'echo hello\n# comment\necho world', stringParserInputCompanion);
404
+ t.is(result.entries.length, 2);
405
+ });
406
+ test('nested parentheses in arithmetic expansion', async (t) => {
407
+ const result = await runParser(bashScriptParser, 'echo $((1 + (2 * 3)))', stringParserInputCompanion);
408
+ const cmd = result.entries[0].pipeline.commands[0];
409
+ const arith = cmd.args[0].parts[0];
410
+ t.is(arith.type, 'arithmeticExpansion');
411
+ if (arith.type === 'arithmeticExpansion') {
412
+ t.is(arith.expression, '1 + (2 * 3)');
413
+ }
414
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,117 @@
1
+ import test from 'ava';
2
+ import { runParser } from './parser.js';
3
+ import { stringParserInputCompanion } from './parserInputCompanion.js';
4
+ import { bashScriptParser } from './bashParser.js';
5
+ // Braced variable expansion: ${VAR}
6
+ test('braced variable expansion', async (t) => {
7
+ const result = await runParser(bashScriptParser, 'echo ${HOME}', stringParserInputCompanion);
8
+ const cmd = result.entries[0].pipeline.commands[0];
9
+ if (cmd.type === 'simple') {
10
+ t.is(cmd.args[0].parts[0].type, 'variableBraced');
11
+ }
12
+ });
13
+ // Braced variable with default: ${VAR:-default}
14
+ test('braced variable with default value', async (t) => {
15
+ const result = await runParser(bashScriptParser, 'echo ${VAR:-default}', stringParserInputCompanion);
16
+ const cmd = result.entries[0].pipeline.commands[0];
17
+ if (cmd.type === 'simple') {
18
+ t.is(cmd.args[0].parts[0].type, 'variableBraced');
19
+ }
20
+ });
21
+ // Arithmetic expansion: $((1+2))
22
+ test('arithmetic expansion', async (t) => {
23
+ const result = await runParser(bashScriptParser, 'echo $((1+2))', stringParserInputCompanion);
24
+ const cmd = result.entries[0].pipeline.commands[0];
25
+ if (cmd.type === 'simple') {
26
+ t.is(cmd.args[0].parts[0].type, 'arithmeticExpansion');
27
+ }
28
+ });
29
+ // Bare $ at end of unquoted word
30
+ test('bare dollar at end of unquoted word', async (t) => {
31
+ const result = await runParser(bashScriptParser, 'echo foo$', stringParserInputCompanion);
32
+ const cmd = result.entries[0].pipeline.commands[0];
33
+ if (cmd.type === 'simple') {
34
+ t.deepEqual(cmd.args[0], {
35
+ parts: [
36
+ { type: 'literal', value: 'foo' },
37
+ { type: 'literal', value: '$' },
38
+ ],
39
+ });
40
+ }
41
+ });
42
+ // Bare $ as its own unquoted word
43
+ test('bare dollar as standalone unquoted word', async (t) => {
44
+ const result = await runParser(bashScriptParser, 'echo $', stringParserInputCompanion);
45
+ const cmd = result.entries[0].pipeline.commands[0];
46
+ if (cmd.type === 'simple') {
47
+ t.deepEqual(cmd.args[0], {
48
+ parts: [
49
+ { type: 'literal', value: '$' },
50
+ ],
51
+ });
52
+ }
53
+ });
54
+ // Comment after command
55
+ test('comment after command', async (t) => {
56
+ const result = await runParser(bashScriptParser, 'echo hello # this is a comment', stringParserInputCompanion);
57
+ const cmd = result.entries[0].pipeline.commands[0];
58
+ if (cmd.type === 'simple') {
59
+ t.is(cmd.args.length, 1);
60
+ t.deepEqual(cmd.args[0], {
61
+ parts: [{ type: 'literal', value: 'hello' }],
62
+ });
63
+ }
64
+ });
65
+ // ANSI-C quoting: $'...'
66
+ test('ansi-c quoting', async (t) => {
67
+ const result = await runParser(bashScriptParser, "echo $'hello\\nworld'", stringParserInputCompanion);
68
+ const cmd = result.entries[0].pipeline.commands[0];
69
+ if (cmd.type === 'simple') {
70
+ t.is(cmd.args.length, 1);
71
+ }
72
+ });
73
+ // Braced variable in double quotes: "${VAR}"
74
+ test('braced variable in double quotes', async (t) => {
75
+ const result = await runParser(bashScriptParser, 'echo "${HOME}"', stringParserInputCompanion);
76
+ const cmd = result.entries[0].pipeline.commands[0];
77
+ if (cmd.type === 'simple') {
78
+ const dq = cmd.args[0].parts[0];
79
+ if (dq.type === 'doubleQuoted') {
80
+ t.is(dq.parts[0].type, 'variableBraced');
81
+ }
82
+ }
83
+ });
84
+ // Arithmetic expansion in double quotes
85
+ test('arithmetic expansion in double quotes', async (t) => {
86
+ const result = await runParser(bashScriptParser, 'echo "$((1+2))"', stringParserInputCompanion);
87
+ const cmd = result.entries[0].pipeline.commands[0];
88
+ if (cmd.type === 'simple') {
89
+ const dq = cmd.args[0].parts[0];
90
+ if (dq.type === 'doubleQuoted') {
91
+ t.is(dq.parts[0].type, 'arithmeticExpansion');
92
+ }
93
+ }
94
+ });
95
+ // Process substitution: <(cmd)
96
+ test('process substitution input', async (t) => {
97
+ const result = await runParser(bashScriptParser, 'diff <(sort file1) <(sort file2)', stringParserInputCompanion);
98
+ t.truthy(result);
99
+ });
100
+ // Line continuation (backslash-newline)
101
+ test('line continuation', async (t) => {
102
+ const result = await runParser(bashScriptParser, 'echo hello \\\nworld', stringParserInputCompanion);
103
+ const cmd = result.entries[0].pipeline.commands[0];
104
+ if (cmd.type === 'simple') {
105
+ t.is(cmd.args.length, 2);
106
+ }
107
+ });
108
+ // Hash in middle of unquoted word is literal, not a comment
109
+ test('hash in middle of unquoted word', async (t) => {
110
+ const result = await runParser(bashScriptParser, 'echo foo#bar', stringParserInputCompanion);
111
+ const cmd = result.entries[0].pipeline.commands[0];
112
+ if (cmd.type === 'simple') {
113
+ t.deepEqual(cmd.args[0], {
114
+ parts: [{ type: 'literal', value: 'foo#bar' }],
115
+ });
116
+ }
117
+ });
@@ -0,0 +1,110 @@
1
+ import { type RawDalvikBytecodeOperation, type IndexResolvedOperation } from '../dalvikBytecodeParser.js';
2
+ import { type CodeUnit, type InstructionIndex } from '../dalvikExecutableParser/typedNumbers.js';
3
+ /**
4
+ * Map from code unit offset to instruction index.
5
+ * Keys are raw code unit offsets, values are instruction indices.
6
+ */
7
+ export type CodeUnitToIndexMap = Map<CodeUnit, InstructionIndex>;
8
+ /**
9
+ * Map from instruction index to code unit offset.
10
+ * Keys are instruction indices, values are raw code unit offsets.
11
+ */
12
+ export type IndexToCodeUnitMap = Map<InstructionIndex, CodeUnit>;
13
+ /**
14
+ * Calculate the size of an operation in code units (16-bit words).
15
+ * Works with any tier of operation (Tier 1, 2, 3, or SmaliCodeOperation).
16
+ */
17
+ export declare function getOperationSizeInCodeUnits(operation: {
18
+ operation: string;
19
+ data?: Uint8Array | number[];
20
+ }): number;
21
+ /**
22
+ * Build mapping from code unit offset to instruction index.
23
+ * Also maps the end position (total code units) to instructions.length.
24
+ */
25
+ export declare function buildCodeUnitToIndexMap(instructions: RawDalvikBytecodeOperation[]): CodeUnitToIndexMap;
26
+ /**
27
+ * Build mapping from instruction index to code unit offset.
28
+ * Also maps instructions.length to the total code units.
29
+ */
30
+ export declare function buildIndexToCodeUnitMap(instructions: RawDalvikBytecodeOperation[]): IndexToCodeUnitMap;
31
+ /**
32
+ * Convert a code unit offset to an instruction index.
33
+ */
34
+ export declare function codeUnitToInstructionIndex(codeUnitOffset: CodeUnit, codeUnitToIndexMap: CodeUnitToIndexMap): InstructionIndex;
35
+ /**
36
+ * Convert an instruction index to a code unit offset.
37
+ */
38
+ export declare function instructionIndexToCodeUnit(instructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): CodeUnit;
39
+ /**
40
+ * Convert a relative branch offset (in code units) to a relative instruction offset.
41
+ * The offset is relative to the source instruction.
42
+ */
43
+ export declare function convertBranchOffsetToInstructionOffset(branchOffsetInCodeUnits: CodeUnit, sourceInstructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap, codeUnitToIndexMap: CodeUnitToIndexMap): InstructionIndex;
44
+ /**
45
+ * Convert a relative instruction offset back to a code unit offset.
46
+ * The offset is relative to the source instruction.
47
+ */
48
+ export declare function convertInstructionOffsetToBranchOffset(instructionOffset: InstructionIndex, sourceInstructionIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): CodeUnit;
49
+ /**
50
+ * Find the switch instruction that references a payload at the given index.
51
+ * Returns the index of the switch instruction.
52
+ */
53
+ export declare function findSwitchInstructionForPayload(instructions: RawDalvikBytecodeOperation[], payloadIndex: InstructionIndex, indexToCodeUnitMap: IndexToCodeUnitMap): InstructionIndex;
54
+ export type ConvertedBranchOffsetOperation<T> = T extends {
55
+ branchOffsetCodeUnit: CodeUnit;
56
+ } ? Omit<T, 'branchOffsetCodeUnit'> & {
57
+ branchOffsetIndex: InstructionIndex;
58
+ } : T extends {
59
+ branchOffsetsCodeUnit: CodeUnit[];
60
+ } ? Omit<T, 'branchOffsetsCodeUnit'> & {
61
+ branchOffsetsIndex: InstructionIndex[];
62
+ } : T;
63
+ export type ConvertedRawDalvikBytecodeOperation = ConvertedBranchOffsetOperation<RawDalvikBytecodeOperation>;
64
+ export type ResolvedBranchOffsetOperation<T> = T extends {
65
+ branchOffsetIndex: InstructionIndex;
66
+ } ? Omit<T, 'branchOffsetIndex'> & {
67
+ targetInstructionIndex: number;
68
+ } : T extends {
69
+ branchOffsetsIndex: InstructionIndex[];
70
+ } ? Omit<T, 'branchOffsetsIndex'> & {
71
+ targetInstructionIndices: number[];
72
+ } : T;
73
+ export type DalvikBytecodeOperation = IndexResolvedOperation<ResolvedBranchOffsetOperation<ConvertedRawDalvikBytecodeOperation>>;
74
+ export type DalvikBytecode = DalvikBytecodeOperation[];
75
+ /**
76
+ * Convert all branch offsets in instructions from code units to instruction offsets.
77
+ * Tier 1 (CodeUnit) -> Tier 2 (InstructionIndex)
78
+ */
79
+ export declare function convertBranchOffsetsToInstructionOffsets(instructions: RawDalvikBytecodeOperation[]): ConvertedRawDalvikBytecodeOperation[];
80
+ /**
81
+ * Unwrap instruction indices to plain numbers and convert relative to absolute.
82
+ * Tier 2 (InstructionIndex, relative) -> Tier 3 (plain number, absolute)
83
+ */
84
+ export declare function unwrapBranchOffsets(instructions: ConvertedRawDalvikBytecodeOperation[]): DalvikBytecodeOperation[];
85
+ /**
86
+ * Wrap plain numbers to instruction indices and convert absolute to relative.
87
+ * Tier 3 (plain number, absolute) -> Tier 2 (InstructionIndex, relative)
88
+ */
89
+ export declare function wrapBranchOffsets(instructions: DalvikBytecodeOperation[]): ConvertedRawDalvikBytecodeOperation[];
90
+ /**
91
+ * Build mapping from instruction index to code unit offset.
92
+ * Works with any tier of operations.
93
+ */
94
+ export declare function buildIndexToCodeUnitMapGeneric(instructions: Array<{
95
+ operation: string;
96
+ data?: Uint8Array | number[];
97
+ }>): IndexToCodeUnitMap;
98
+ /**
99
+ * Build mapping from instruction index to code unit offset for Tier 2 operations.
100
+ */
101
+ export declare function buildIndexToCodeUnitMapFromConverted(instructions: ConvertedRawDalvikBytecodeOperation[]): IndexToCodeUnitMap;
102
+ /**
103
+ * Build mapping from instruction index to code unit offset for Tier 3 operations.
104
+ */
105
+ export declare function buildIndexToCodeUnitMapFromResolved(instructions: DalvikBytecodeOperation[]): IndexToCodeUnitMap;
106
+ /**
107
+ * Convert all branch offsets in instructions from instruction offsets back to code units.
108
+ * Tier 2 (InstructionIndex) -> Tier 1 (CodeUnit)
109
+ */
110
+ export declare function convertInstructionOffsetsToBranchOffsets(instructions: ConvertedRawDalvikBytecodeOperation[]): RawDalvikBytecodeOperation[];