@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
@@ -0,0 +1,335 @@
1
+ import { setParserName } from './parser.js';
2
+ import { createExactSequenceParser } from './exactSequenceParser.js';
3
+ import { promiseCompose } from './promiseCompose.js';
4
+ import { createTupleParser } from './tupleParser.js';
5
+ import { createDisjunctionParser } from './disjunctionParser.js';
6
+ import { createArrayParser } from './arrayParser.js';
7
+ import { createParserAccessorParser } from './parserAccessorParser.js';
8
+ import { createOptionalParser } from './optionalParser.js';
9
+ import { createRegExpParser } from './regexpParser.js';
10
+ import { createNonEmptyArrayParser } from './nonEmptyArrayParser.js';
11
+ import { createSeparatedNonEmptyArrayParser } from './separatedNonEmptyArrayParser.js';
12
+ import { createObjectParser } from './objectParser.js';
13
+ // Whitespace (spaces, tabs, and line continuations - not bare newlines which are significant)
14
+ const bashInlineWhitespaceParser = promiseCompose(createRegExpParser(/(?:[ \t]|\\\n)+/), match => match[0]);
15
+ const bashOptionalInlineWhitespaceParser = promiseCompose(createRegExpParser(/(?:[ \t]|\\\n)*/), match => match[0]);
16
+ // Newline
17
+ const bashNewlineParser = promiseCompose(createRegExpParser(/\n/), match => match[0]);
18
+ // Word characters (unquoted, no special chars)
19
+ // Note: {} are excluded so brace groups are parsed correctly
20
+ // # is excluded from the first character (starts a comment) but allowed mid-word
21
+ const bashUnquotedWordCharsParser = promiseCompose(createRegExpParser(/[^\s\n|&;<>(){}$`"'\\#][^\s\n|&;<>(){}$`"'\\]*/), match => match[0]);
22
+ // Single quoted string: '...'
23
+ const bashSingleQuotedParser = createObjectParser({
24
+ type: 'singleQuoted',
25
+ _open: createExactSequenceParser("'"),
26
+ value: promiseCompose(createRegExpParser(/[^']*/), match => match[0]),
27
+ _close: createExactSequenceParser("'"),
28
+ });
29
+ // Variable name
30
+ const bashVariableNameParser = promiseCompose(createRegExpParser(/[a-zA-Z_][a-zA-Z0-9_]*|[0-9]+|[@*#?$!-]/), match => match[0]);
31
+ // Simple variable: $var
32
+ const bashSimpleVariableParser = createObjectParser({
33
+ type: 'variable',
34
+ _dollar: createExactSequenceParser('$'),
35
+ name: bashVariableNameParser,
36
+ });
37
+ // Command substitution: $(...)
38
+ const bashCommandSubstitutionParser = createObjectParser({
39
+ type: 'commandSubstitution',
40
+ _open: createExactSequenceParser('$('),
41
+ _ws1: bashOptionalInlineWhitespaceParser,
42
+ command: createParserAccessorParser(() => bashCommandParser),
43
+ _ws2: bashOptionalInlineWhitespaceParser,
44
+ _close: createExactSequenceParser(')'),
45
+ });
46
+ // Backtick substitution: `...`
47
+ const bashBacktickSubstitutionParser = createObjectParser({
48
+ type: 'backtickSubstitution',
49
+ _open: createExactSequenceParser('`'),
50
+ command: createParserAccessorParser(() => bashCommandParser),
51
+ _close: createExactSequenceParser('`'),
52
+ });
53
+ // Braced variable expansion: ${VAR} or ${VAR:-default}
54
+ const bashBracedVariableParser = createObjectParser({
55
+ type: 'variableBraced',
56
+ _open: createExactSequenceParser('${'),
57
+ name: bashVariableNameParser,
58
+ operator: createOptionalParser(promiseCompose(createRegExpParser(/:-|:=|:\+|:\?|-|=|\+|\?|##|#|%%|%/), match => match[0])),
59
+ operand: createOptionalParser(createParserAccessorParser(() => bashWordParser)),
60
+ _close: createExactSequenceParser('}'),
61
+ });
62
+ // Arithmetic expansion: $((expression))
63
+ const bashArithmeticExpansionParser = createObjectParser({
64
+ type: 'arithmeticExpansion',
65
+ _open: createExactSequenceParser('$(('),
66
+ expression: promiseCompose(createRegExpParser(/(?:[^)]|\)(?!\)))*/), match => match[0]),
67
+ _close: createExactSequenceParser('))'),
68
+ });
69
+ // ANSI-C quoting: $'...'
70
+ const bashAnsiCQuotedParser = createObjectParser({
71
+ type: 'singleQuoted',
72
+ _prefix: createExactSequenceParser('$'),
73
+ _open: createExactSequenceParser("'"),
74
+ value: promiseCompose(createRegExpParser(/(?:[^'\\]|\\.)*/), match => match[0]),
75
+ _close: createExactSequenceParser("'"),
76
+ });
77
+ // Process substitution: <(cmd) or >(cmd)
78
+ const bashProcessSubstitutionParser = createObjectParser({
79
+ type: 'processSubstitution',
80
+ direction: promiseCompose(createRegExpParser(/[<>](?=\()/), match => match[0]),
81
+ _open: createExactSequenceParser('('),
82
+ _ws1: bashOptionalInlineWhitespaceParser,
83
+ command: createParserAccessorParser(() => bashCommandParser),
84
+ _ws2: bashOptionalInlineWhitespaceParser,
85
+ _close: createExactSequenceParser(')'),
86
+ });
87
+ // Double quoted string parts (inside "...")
88
+ const bashDoubleQuotedPartParser = createDisjunctionParser([
89
+ bashBracedVariableParser,
90
+ bashArithmeticExpansionParser,
91
+ bashSimpleVariableParser,
92
+ bashCommandSubstitutionParser,
93
+ bashBacktickSubstitutionParser,
94
+ // Escape sequences in double quotes
95
+ promiseCompose(createRegExpParser(/\\[\\$`"!\n]/), match => ({
96
+ type: 'literal',
97
+ value: match[0].slice(1),
98
+ })),
99
+ // Literal text (no special chars)
100
+ promiseCompose(createRegExpParser(/[^$`"\\]+/), match => ({
101
+ type: 'literal',
102
+ value: match[0],
103
+ })),
104
+ // Bare $ not followed by a valid expansion start (e.g. $" at end of double-quoted string)
105
+ promiseCompose(createRegExpParser(/\$/), () => ({
106
+ type: 'literal',
107
+ value: '$',
108
+ })),
109
+ // Bare \ not followed by a recognized escape character (treated as literal backslash in bash)
110
+ promiseCompose(createRegExpParser(/\\/), () => ({
111
+ type: 'literal',
112
+ value: '\\',
113
+ })),
114
+ ]);
115
+ // Double quoted string: "..."
116
+ const bashDoubleQuotedParser = createObjectParser({
117
+ type: 'doubleQuoted',
118
+ _open: createExactSequenceParser('"'),
119
+ parts: createArrayParser(bashDoubleQuotedPartParser),
120
+ _close: createExactSequenceParser('"'),
121
+ });
122
+ // Literal word part (unquoted)
123
+ const bashLiteralWordPartParser = createObjectParser({
124
+ type: 'literal',
125
+ value: bashUnquotedWordCharsParser,
126
+ });
127
+ // Escape sequence outside quotes
128
+ const bashEscapeParser = promiseCompose(createRegExpParser(/\\./), match => ({
129
+ type: 'literal',
130
+ value: match[0].slice(1),
131
+ }));
132
+ // Word part (any part of a word)
133
+ const bashWordPartParser = createDisjunctionParser([
134
+ bashAnsiCQuotedParser,
135
+ bashSingleQuotedParser,
136
+ bashDoubleQuotedParser,
137
+ bashBracedVariableParser,
138
+ bashArithmeticExpansionParser,
139
+ bashCommandSubstitutionParser,
140
+ bashBacktickSubstitutionParser,
141
+ bashSimpleVariableParser,
142
+ bashProcessSubstitutionParser,
143
+ bashEscapeParser,
144
+ bashLiteralWordPartParser,
145
+ // Bare $ not followed by a valid expansion start
146
+ promiseCompose(createRegExpParser(/\$/), () => ({
147
+ type: 'literal',
148
+ value: '$',
149
+ })),
150
+ ]);
151
+ // Word (sequence of word parts)
152
+ export const bashWordParser = createObjectParser({
153
+ parts: createNonEmptyArrayParser(bashWordPartParser),
154
+ });
155
+ setParserName(bashWordParser, 'bashWordParser');
156
+ // Assignment: NAME=value or NAME=
157
+ const bashAssignmentParser = createObjectParser({
158
+ name: promiseCompose(createRegExpParser(/[a-zA-Z_][a-zA-Z0-9_]*=/), match => match[0].slice(0, -1)),
159
+ value: createOptionalParser(bashWordParser),
160
+ });
161
+ // Redirect operators
162
+ const bashRedirectOperatorParser = createDisjunctionParser([
163
+ promiseCompose(createExactSequenceParser('>>'), () => '>>'),
164
+ promiseCompose(createExactSequenceParser('>&'), () => '>&'),
165
+ promiseCompose(createExactSequenceParser('>|'), () => '>|'),
166
+ promiseCompose(createExactSequenceParser('>'), () => '>'),
167
+ promiseCompose(createExactSequenceParser('<<<'), () => '<<<'),
168
+ promiseCompose(createExactSequenceParser('<<'), () => '<<'),
169
+ promiseCompose(createExactSequenceParser('<&'), () => '<&'),
170
+ promiseCompose(createExactSequenceParser('<'), () => '<'),
171
+ ]);
172
+ // Redirect: [n]op word
173
+ const bashRedirectParser = createObjectParser({
174
+ fd: createOptionalParser(promiseCompose(createRegExpParser(/[0-9]+/), match => Number.parseInt(match[0], 10))),
175
+ operator: bashRedirectOperatorParser,
176
+ _ws: bashOptionalInlineWhitespaceParser,
177
+ target: bashWordParser,
178
+ });
179
+ // Word with optional trailing whitespace - for use in arrays
180
+ const bashWordWithWhitespaceParser = promiseCompose(createTupleParser([
181
+ bashWordParser,
182
+ bashOptionalInlineWhitespaceParser,
183
+ ]), ([word]) => word);
184
+ // Redirect with optional trailing whitespace
185
+ const bashRedirectWithWhitespaceParser = promiseCompose(createTupleParser([
186
+ bashRedirectParser,
187
+ bashOptionalInlineWhitespaceParser,
188
+ ]), ([redirect]) => redirect);
189
+ // Word or redirect - for interleaved parsing in simple commands
190
+ const bashWordOrRedirectParser = createDisjunctionParser([
191
+ createObjectParser({ type: 'redirect', redirect: bashRedirectWithWhitespaceParser }),
192
+ createObjectParser({ type: 'word', word: bashWordWithWhitespaceParser }),
193
+ ]);
194
+ // Simple command: [assignments] [name] [args] [redirects]
195
+ export const bashSimpleCommandParser = promiseCompose(createTupleParser([
196
+ // Assignments at the start
197
+ createArrayParser(promiseCompose(createTupleParser([
198
+ bashAssignmentParser,
199
+ bashOptionalInlineWhitespaceParser,
200
+ ]), ([assignment]) => assignment)),
201
+ // Command name, args, and redirects (interleaved)
202
+ createArrayParser(bashWordOrRedirectParser),
203
+ ]), ([assignments, items]) => {
204
+ const words = [];
205
+ const redirects = [];
206
+ for (const item of items) {
207
+ if (item.type === 'word') {
208
+ words.push(item.word);
209
+ }
210
+ else {
211
+ redirects.push(item.redirect);
212
+ }
213
+ }
214
+ const [name, ...args] = words;
215
+ return {
216
+ type: 'simple',
217
+ name,
218
+ args,
219
+ redirects,
220
+ assignments,
221
+ };
222
+ });
223
+ setParserName(bashSimpleCommandParser, 'bashSimpleCommandParser');
224
+ // Subshell: ( command )
225
+ const bashSubshellParser = createObjectParser({
226
+ type: 'subshell',
227
+ _open: createExactSequenceParser('('),
228
+ _ws1: bashOptionalInlineWhitespaceParser,
229
+ body: createParserAccessorParser(() => bashCommandParser),
230
+ _ws2: bashOptionalInlineWhitespaceParser,
231
+ _close: createExactSequenceParser(')'),
232
+ });
233
+ setParserName(bashSubshellParser, 'bashSubshellParser');
234
+ // Brace group: { command; }
235
+ const bashBraceGroupParser = createObjectParser({
236
+ type: 'braceGroup',
237
+ _open: createExactSequenceParser('{'),
238
+ _ws1: bashInlineWhitespaceParser,
239
+ body: createParserAccessorParser(() => bashCommandParser),
240
+ _ws2: bashOptionalInlineWhitespaceParser,
241
+ _semi: createOptionalParser(createExactSequenceParser(';')),
242
+ _ws3: bashOptionalInlineWhitespaceParser,
243
+ _close: createExactSequenceParser('}'),
244
+ });
245
+ setParserName(bashBraceGroupParser, 'bashBraceGroupParser');
246
+ // Command unit: simple command, subshell, or brace group
247
+ const bashCommandUnitParser = createDisjunctionParser([
248
+ bashSubshellParser,
249
+ bashBraceGroupParser,
250
+ bashSimpleCommandParser,
251
+ ]);
252
+ setParserName(bashCommandUnitParser, 'bashCommandUnitParser');
253
+ // Single pipe (not ||) - matches | only when not followed by another |
254
+ const bashSinglePipeParser = promiseCompose(createRegExpParser(/\|(?!\|)/), match => match[0]);
255
+ // Pipeline: [!] cmd [| cmd]...
256
+ const bashPipelineParser = promiseCompose(createTupleParser([
257
+ createOptionalParser(promiseCompose(createTupleParser([
258
+ createExactSequenceParser('!'),
259
+ bashInlineWhitespaceParser,
260
+ ]), () => true)),
261
+ createSeparatedNonEmptyArrayParser(bashCommandUnitParser, createTupleParser([
262
+ bashOptionalInlineWhitespaceParser,
263
+ bashSinglePipeParser,
264
+ bashOptionalInlineWhitespaceParser,
265
+ ])),
266
+ ]), ([negated, commands]) => ({
267
+ type: 'pipeline',
268
+ negated: negated ?? false,
269
+ commands,
270
+ }));
271
+ setParserName(bashPipelineParser, 'bashPipelineParser');
272
+ // Command list separator
273
+ const bashListSeparatorParser = createDisjunctionParser([
274
+ promiseCompose(createExactSequenceParser('&&'), () => '&&'),
275
+ promiseCompose(createExactSequenceParser('||'), () => '||'),
276
+ promiseCompose(createExactSequenceParser(';'), () => ';'),
277
+ promiseCompose(createExactSequenceParser('&'), () => '&'),
278
+ promiseCompose(bashNewlineParser, () => '\n'),
279
+ ]);
280
+ // Command list: pipeline [sep pipeline]...
281
+ const bashCommandListParser = promiseCompose(createTupleParser([
282
+ bashPipelineParser,
283
+ createArrayParser(createObjectParser({
284
+ _ws1: bashOptionalInlineWhitespaceParser,
285
+ separator: bashListSeparatorParser,
286
+ _ws2: bashOptionalInlineWhitespaceParser,
287
+ pipeline: bashPipelineParser,
288
+ })),
289
+ createOptionalParser(promiseCompose(createTupleParser([
290
+ bashOptionalInlineWhitespaceParser,
291
+ bashListSeparatorParser,
292
+ ]), ([, separator]) => separator)),
293
+ ]), ([firstPipeline, rest, trailingSeparator]) => {
294
+ const entries = [];
295
+ if (rest.length === 0) {
296
+ entries.push({
297
+ pipeline: firstPipeline,
298
+ separator: trailingSeparator ?? undefined,
299
+ });
300
+ }
301
+ else {
302
+ entries.push({
303
+ pipeline: firstPipeline,
304
+ separator: rest[0].separator,
305
+ });
306
+ for (let i = 0; i < rest.length - 1; i++) {
307
+ entries.push({
308
+ pipeline: rest[i].pipeline,
309
+ separator: rest[i + 1].separator,
310
+ });
311
+ }
312
+ entries.push({
313
+ pipeline: rest[rest.length - 1].pipeline,
314
+ separator: trailingSeparator ?? undefined,
315
+ });
316
+ }
317
+ return {
318
+ type: 'list',
319
+ entries,
320
+ };
321
+ });
322
+ setParserName(bashCommandListParser, 'bashCommandListParser');
323
+ // Top-level command parser
324
+ export const bashCommandParser = bashCommandListParser;
325
+ setParserName(bashCommandParser, 'bashCommandParser');
326
+ // Comment: # through end of line
327
+ const bashOptionalCommentParser = createOptionalParser(promiseCompose(createRegExpParser(/#[^\n]*/), match => match[0]));
328
+ // Script parser (handles leading/trailing whitespace and comments)
329
+ export const bashScriptParser = promiseCompose(createTupleParser([
330
+ bashOptionalInlineWhitespaceParser,
331
+ bashCommandParser,
332
+ bashOptionalInlineWhitespaceParser,
333
+ bashOptionalCommentParser,
334
+ ]), ([, command]) => command);
335
+ setParserName(bashScriptParser, 'bashScriptParser');
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,343 @@
1
+ import test from 'ava';
2
+ import { runParser } from './parser.js';
3
+ import { stringParserInputCompanion } from './parserInputCompanion.js';
4
+ import { bashScriptParser, bashWordParser, bashSimpleCommandParser } from './bashParser.js';
5
+ test('simple command parser - single word', async (t) => {
6
+ const result = await runParser(bashSimpleCommandParser, 'cmd', stringParserInputCompanion, { errorStack: true });
7
+ t.is(result.type, 'simple');
8
+ t.deepEqual(result.name, { parts: [{ type: 'literal', value: 'cmd' }] });
9
+ });
10
+ test('simple command parser - two words', async (t) => {
11
+ const result = await runParser(bashSimpleCommandParser, 'echo hello', stringParserInputCompanion);
12
+ t.is(result.type, 'simple');
13
+ t.deepEqual(result.name, { parts: [{ type: 'literal', value: 'echo' }] });
14
+ t.is(result.args.length, 1);
15
+ });
16
+ test('word parser - simple literal', async (t) => {
17
+ const result = await runParser(bashWordParser, 'hello', stringParserInputCompanion);
18
+ t.deepEqual(result, {
19
+ parts: [{ type: 'literal', value: 'hello' }],
20
+ });
21
+ });
22
+ test('word parser - variable', async (t) => {
23
+ const result = await runParser(bashWordParser, '$HOME', stringParserInputCompanion);
24
+ t.deepEqual(result, {
25
+ parts: [{ type: 'variable', name: 'HOME' }],
26
+ });
27
+ });
28
+ test('simple command', async (t) => {
29
+ const result = await runParser(bashScriptParser, 'echo hello', stringParserInputCompanion);
30
+ t.deepEqual(result, {
31
+ type: 'list',
32
+ entries: [{
33
+ pipeline: {
34
+ type: 'pipeline',
35
+ negated: false,
36
+ commands: [{
37
+ type: 'simple',
38
+ name: { parts: [{ type: 'literal', value: 'echo' }] },
39
+ args: [{ parts: [{ type: 'literal', value: 'hello' }] }],
40
+ redirects: [],
41
+ assignments: [],
42
+ }],
43
+ },
44
+ separator: undefined,
45
+ }],
46
+ });
47
+ });
48
+ test('simple command with multiple args', async (t) => {
49
+ const result = await runParser(bashScriptParser, 'echo hello world', stringParserInputCompanion);
50
+ t.is(result.entries[0].pipeline.commands[0].type, 'simple');
51
+ const cmd = result.entries[0].pipeline.commands[0];
52
+ if (cmd.type === 'simple') {
53
+ t.is(cmd.args.length, 2);
54
+ }
55
+ });
56
+ test('pipeline', async (t) => {
57
+ const result = await runParser(bashScriptParser, 'cat file | grep pattern', stringParserInputCompanion);
58
+ t.is(result.entries[0].pipeline.commands.length, 2);
59
+ });
60
+ test('redirect output', async (t) => {
61
+ const result = await runParser(bashScriptParser, 'echo foo > file', stringParserInputCompanion);
62
+ const cmd = result.entries[0].pipeline.commands[0];
63
+ if (cmd.type === 'simple') {
64
+ t.is(cmd.redirects.length, 1);
65
+ t.is(cmd.redirects[0].operator, '>');
66
+ }
67
+ });
68
+ test('redirect with fd', async (t) => {
69
+ const result = await runParser(bashScriptParser, 'cmd 2>&1', stringParserInputCompanion);
70
+ const cmd = result.entries[0].pipeline.commands[0];
71
+ if (cmd.type === 'simple') {
72
+ t.is(cmd.redirects.length, 1);
73
+ t.is(cmd.redirects[0].fd, 2);
74
+ t.is(cmd.redirects[0].operator, '>&');
75
+ }
76
+ });
77
+ test('single quoted string', async (t) => {
78
+ const result = await runParser(bashScriptParser, "echo 'hello world'", stringParserInputCompanion);
79
+ const cmd = result.entries[0].pipeline.commands[0];
80
+ if (cmd.type === 'simple') {
81
+ t.deepEqual(cmd.args[0], {
82
+ parts: [{ type: 'singleQuoted', value: 'hello world' }],
83
+ });
84
+ }
85
+ });
86
+ test('double quoted string with variable', async (t) => {
87
+ const result = await runParser(bashScriptParser, 'echo "hello $name"', stringParserInputCompanion);
88
+ const cmd = result.entries[0].pipeline.commands[0];
89
+ if (cmd.type === 'simple') {
90
+ t.deepEqual(cmd.args[0], {
91
+ parts: [{
92
+ type: 'doubleQuoted',
93
+ parts: [
94
+ { type: 'literal', value: 'hello ' },
95
+ { type: 'variable', name: 'name' },
96
+ ],
97
+ }],
98
+ });
99
+ }
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
+ });
150
+ test('simple variable', async (t) => {
151
+ const result = await runParser(bashScriptParser, 'echo $HOME', stringParserInputCompanion);
152
+ const cmd = result.entries[0].pipeline.commands[0];
153
+ if (cmd.type === 'simple') {
154
+ t.deepEqual(cmd.args[0], {
155
+ parts: [{ type: 'variable', name: 'HOME' }],
156
+ });
157
+ }
158
+ });
159
+ test('command substitution', async (t) => {
160
+ const result = await runParser(bashScriptParser, 'echo $(pwd)', stringParserInputCompanion);
161
+ const cmd = result.entries[0].pipeline.commands[0];
162
+ if (cmd.type === 'simple') {
163
+ t.is(cmd.args[0].parts[0].type, 'commandSubstitution');
164
+ }
165
+ });
166
+ test('backtick substitution', async (t) => {
167
+ const result = await runParser(bashScriptParser, 'echo `pwd`', stringParserInputCompanion);
168
+ const cmd = result.entries[0].pipeline.commands[0];
169
+ if (cmd.type === 'simple') {
170
+ t.is(cmd.args[0].parts[0].type, 'backtickSubstitution');
171
+ }
172
+ });
173
+ test('subshell', async (t) => {
174
+ const result = await runParser(bashScriptParser, '(cd dir; pwd)', stringParserInputCompanion);
175
+ t.is(result.entries[0].pipeline.commands[0].type, 'subshell');
176
+ });
177
+ test('brace group', async (t) => {
178
+ const result = await runParser(bashScriptParser, '{ echo hello; }', stringParserInputCompanion);
179
+ t.is(result.entries[0].pipeline.commands[0].type, 'braceGroup');
180
+ });
181
+ test('command list with &&', async (t) => {
182
+ const result = await runParser(bashScriptParser, 'cmd1 && cmd2', stringParserInputCompanion);
183
+ t.is(result.entries.length, 2);
184
+ t.is(result.entries[0].separator, '&&');
185
+ });
186
+ test('command list with ||', async (t) => {
187
+ const result = await runParser(bashScriptParser, 'cmd1 || cmd2', stringParserInputCompanion);
188
+ t.is(result.entries.length, 2);
189
+ t.is(result.entries[0].separator, '||');
190
+ });
191
+ test('command list with ;', async (t) => {
192
+ const result = await runParser(bashScriptParser, 'cmd1; cmd2', stringParserInputCompanion);
193
+ t.is(result.entries.length, 2);
194
+ t.is(result.entries[0].separator, ';');
195
+ });
196
+ test('background command', async (t) => {
197
+ const result = await runParser(bashScriptParser, 'cmd &', stringParserInputCompanion);
198
+ t.is(result.entries[0].separator, '&');
199
+ });
200
+ test('assignment', async (t) => {
201
+ const result = await runParser(bashScriptParser, 'VAR=value cmd', stringParserInputCompanion);
202
+ const cmd = result.entries[0].pipeline.commands[0];
203
+ if (cmd.type === 'simple') {
204
+ t.is(cmd.assignments.length, 1);
205
+ t.is(cmd.assignments[0].name, 'VAR');
206
+ }
207
+ });
208
+ test('negated pipeline', async (t) => {
209
+ const result = await runParser(bashScriptParser, '! cmd', stringParserInputCompanion);
210
+ t.is(result.entries[0].pipeline.negated, true);
211
+ });
212
+ test('complex pipeline with redirects', async (t) => {
213
+ const result = await runParser(bashScriptParser, 'cat file 2>/dev/null | grep pattern | sort > output', stringParserInputCompanion);
214
+ t.is(result.entries[0].pipeline.commands.length, 3);
215
+ });
216
+ test('[[ treated as command name', async (t) => {
217
+ const result = await runParser(bashScriptParser, '[[ -f file ]]', stringParserInputCompanion);
218
+ const cmd = result.entries[0].pipeline.commands[0];
219
+ if (cmd.type === 'simple') {
220
+ t.deepEqual(cmd.name, { parts: [{ type: 'literal', value: '[[' }] });
221
+ t.is(cmd.args.length, 3); // -f, file, ]]
222
+ }
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
+ });
337
+ test('if treated as command name', async (t) => {
338
+ const result = await runParser(bashScriptParser, 'if true', stringParserInputCompanion);
339
+ const cmd = result.entries[0].pipeline.commands[0];
340
+ if (cmd.type === 'simple') {
341
+ t.deepEqual(cmd.name, { parts: [{ type: 'literal', value: 'if' }] });
342
+ }
343
+ });
@@ -0,0 +1 @@
1
+ export {};