@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.
- package/.claude/settings.local.json +24 -0
- package/.github/workflows/main.yml +1 -0
- package/build/androidPackageParser.js +30 -32
- package/build/arbitraryDalvikBytecode.d.ts +3 -3
- package/build/arbitraryDalvikBytecode.js +33 -27
- package/build/arbitraryDalvikExecutable.js +55 -17
- package/build/arbitraryJava.d.ts +31 -0
- package/build/arbitraryJava.js +532 -0
- package/build/arbitraryJavaScript.d.ts +3 -0
- package/build/arbitraryJavaScript.js +263 -0
- package/build/arbitraryJavascript.d.ts +3 -0
- package/build/arbitraryJavascript.js +263 -0
- package/build/arbitraryZig.d.ts +3 -0
- package/build/arbitraryZig.js +240 -0
- package/build/arbitraryZipStream.d.ts +1 -1
- package/build/arrayParser.js +72 -13
- package/build/backsmali.d.ts +4 -3
- package/build/backsmali.js +26 -6
- package/build/bash.d.ts +6 -1
- package/build/bashParser.js +414 -131
- package/build/bashParser.test.js +233 -0
- package/build/bashParserEdgeCases.test.d.ts +1 -0
- package/build/bashParserEdgeCases.test.js +117 -0
- package/build/dalvikBytecodeParser/addressConversion.d.ts +110 -0
- package/build/dalvikBytecodeParser/addressConversion.js +334 -0
- package/build/dalvikBytecodeParser/formatParsers.d.ts +7 -6
- package/build/dalvikBytecodeParser/formatParsers.js +13 -14
- package/build/dalvikBytecodeParser.d.ts +60 -31
- package/build/dalvikBytecodeParser.js +92 -35
- package/build/dalvikBytecodeParser.test-d.d.ts +1 -0
- package/build/dalvikBytecodeParser.test-d.js +268 -0
- package/build/dalvikBytecodeUnparser/formatUnparsers.d.ts +9 -8
- package/build/dalvikBytecodeUnparser/formatUnparsers.js +13 -12
- package/build/dalvikBytecodeUnparser.d.ts +2 -2
- package/build/dalvikBytecodeUnparser.js +23 -23
- package/build/dalvikBytecodeUnparser.test.js +7 -7
- package/build/dalvikExecutable.d.ts +3 -3
- package/build/dalvikExecutable.test-d.d.ts +1 -0
- package/build/dalvikExecutable.test-d.js +59 -0
- package/build/dalvikExecutableParser/typedNumbers.d.ts +18 -0
- package/build/dalvikExecutableParser/typedNumbers.js +3 -0
- package/build/dalvikExecutableParser.d.ts +2 -1
- package/build/dalvikExecutableParser.js +96 -77
- package/build/dalvikExecutableParser.test.js +24 -3
- package/build/dalvikExecutableParserAgainstSmaliParser.test.js +3 -0
- package/build/dalvikExecutableUnparser/poolScanners.d.ts +2 -2
- package/build/dalvikExecutableUnparser/sectionUnparsers.d.ts +3 -3
- package/build/dalvikExecutableUnparser/sectionUnparsers.js +26 -11
- package/build/dalvikExecutableUnparser.d.ts +2 -2
- package/build/dalvikExecutableUnparser.test.js +2 -1
- package/build/disjunctionParser.d.ts +5 -3
- package/build/disjunctionParser.js +79 -17
- package/build/disjunctionParser.test-d.d.ts +1 -0
- package/build/disjunctionParser.test-d.js +72 -0
- package/build/elementSwitchParser.d.ts +4 -0
- package/build/{exactElementSwitchParser.js → elementSwitchParser.js} +3 -4
- package/build/elementSwitchParser.test-d.d.ts +1 -0
- package/build/elementSwitchParser.test-d.js +44 -0
- package/build/exactSequenceParser.d.ts +4 -2
- package/build/exactSequenceParser.test-d.d.ts +1 -0
- package/build/exactSequenceParser.test-d.js +36 -0
- package/build/fetchCid.js +2 -66
- package/build/index.d.ts +4 -2
- package/build/index.js +3 -1
- package/build/index.test.js +16 -1
- package/build/inputReader.d.ts +10 -0
- package/build/inputReader.js +36 -0
- package/build/java.d.ts +502 -0
- package/build/java.js +2 -0
- package/build/javaKeyStoreParser.js +14 -17
- package/build/javaParser.d.ts +51 -0
- package/build/javaParser.js +1538 -0
- package/build/javaParser.test.d.ts +1 -0
- package/build/javaParser.test.js +1287 -0
- package/build/javaScript.d.ts +35 -0
- package/build/javaScript.js +1 -0
- package/build/javaScriptParser.d.ts +9 -0
- package/build/javaScriptParser.js +34 -0
- package/build/javaScriptUnparser.d.ts +3 -0
- package/build/javaScriptUnparser.js +4 -0
- package/build/javaScriptUnparser.test.d.ts +1 -0
- package/build/javaScriptUnparser.test.js +24 -0
- package/build/javaUnparser.d.ts +2 -0
- package/build/javaUnparser.js +519 -0
- package/build/javaUnparser.test.d.ts +1 -0
- package/build/javaUnparser.test.js +24 -0
- package/build/javascript.d.ts +35 -0
- package/build/javascript.js +1 -0
- package/build/javascriptParser.d.ts +9 -0
- package/build/javascriptParser.js +34 -0
- package/build/javascriptUnparser.d.ts +3 -0
- package/build/javascriptUnparser.js +4 -0
- package/build/javascriptUnparser.test.d.ts +1 -0
- package/build/javascriptUnparser.test.js +24 -0
- package/build/jsonParser.js +2 -12
- package/build/lazyMessageError.d.ts +3 -0
- package/build/lookaheadParser.js +60 -3
- package/build/negativeLookaheadParser.js +70 -11
- package/build/nonEmptyArrayParser.js +72 -13
- package/build/objectParser.d.ts +12 -0
- package/build/objectParser.js +31 -0
- package/build/objectParser.test-d.d.ts +1 -0
- package/build/objectParser.test-d.js +112 -0
- package/build/objectParser.test.d.ts +1 -0
- package/build/objectParser.test.js +55 -0
- package/build/optionalParser.js +69 -10
- package/build/parser.d.ts +4 -0
- package/build/parser.js +3 -1
- package/build/parser.test.js +114 -1
- package/build/parserConsumedSequenceParser.js +66 -7
- package/build/parserContext.d.ts +6 -0
- package/build/parserContext.js +20 -11
- package/build/parserError.d.ts +119 -27
- package/build/parserError.js +16 -8
- package/build/predicateElementParser.d.ts +3 -0
- package/build/predicateElementParser.js +10 -0
- package/build/regexpParser.js +33 -3
- package/build/regexpParser.test.js +31 -0
- package/build/regularExpressionParser.js +35 -15
- package/build/separatedArrayParser.js +73 -14
- package/build/separatedNonEmptyArrayParser.js +73 -14
- package/build/sliceBoundedParser.js +62 -5
- package/build/smaliParser.d.ts +7 -7
- package/build/smaliParser.js +185 -268
- package/build/smaliParser.test.js +58 -0
- package/build/stringEscapes.d.ts +5 -0
- package/build/stringEscapes.js +244 -0
- package/build/symbolicExpression.d.ts +29 -0
- package/build/symbolicExpression.js +1 -0
- package/build/symbolicExpressionParser.d.ts +4 -0
- package/build/symbolicExpressionParser.js +123 -0
- package/build/symbolicExpressionParser.test.d.ts +1 -0
- package/build/symbolicExpressionParser.test.js +289 -0
- package/build/terminatedArrayParser.js +113 -38
- package/build/terminatedArrayParser.test.js +4 -2
- package/build/tupleParser.d.ts +7 -15
- package/build/tupleParser.js +1 -0
- package/build/unionParser.d.ts +5 -3
- package/build/unionParser.js +7 -2
- package/build/unionParser.test-d.d.ts +1 -0
- package/build/unionParser.test-d.js +72 -0
- package/build/unionParser.test.js +10 -11
- package/build/zig.d.ts +280 -0
- package/build/zig.js +2 -0
- package/build/zigParser.d.ts +3 -0
- package/build/zigParser.js +1119 -0
- package/build/zigParser.test.d.ts +1 -0
- package/build/zigParser.test.js +1590 -0
- package/build/zigUnparser.d.ts +2 -0
- package/build/zigUnparser.js +460 -0
- package/build/zigUnparser.test.d.ts +1 -0
- package/build/zigUnparser.test.js +24 -0
- package/build/zipParser.js +19 -32
- package/build/zipUnparser.js +19 -7
- package/build/zipUnparser.test.js +1 -1
- package/node_modules-@types/s-expression/index.d.ts +5 -0
- package/package.json +24 -6
- package/src/androidPackageParser.ts +33 -60
- package/src/arbitraryDalvikBytecode.ts +39 -31
- package/src/arbitraryDalvikExecutable.ts +65 -20
- package/src/arbitraryJava.ts +804 -0
- package/src/arbitraryJavaScript.ts +410 -0
- package/src/arbitraryZig.ts +380 -0
- package/src/arrayParser.ts +1 -3
- package/src/backsmali.ts +35 -4
- package/src/bash.ts +8 -1
- package/src/bashParser.test.ts +396 -0
- package/src/bashParser.ts +564 -199
- package/src/dalvikBytecodeParser/addressConversion.ts +496 -0
- package/src/dalvikBytecodeParser/formatParsers.ts +19 -29
- package/src/dalvikBytecodeParser.test-d.ts +310 -0
- package/src/dalvikBytecodeParser.ts +194 -69
- package/src/dalvikBytecodeUnparser/formatUnparsers.ts +27 -26
- package/src/dalvikBytecodeUnparser.test.ts +7 -7
- package/src/dalvikBytecodeUnparser.ts +31 -30
- package/src/dalvikExecutable.test-d.ts +132 -0
- package/src/dalvikExecutable.ts +3 -3
- package/src/dalvikExecutableParser/typedNumbers.ts +11 -0
- package/src/dalvikExecutableParser.test.ts +37 -3
- package/src/dalvikExecutableParser.test.ts.md +163 -2
- package/src/dalvikExecutableParser.test.ts.snap +0 -0
- package/src/dalvikExecutableParser.ts +121 -139
- package/src/dalvikExecutableParserAgainstSmaliParser.test.ts +4 -0
- package/src/dalvikExecutableUnparser/poolScanners.ts +6 -6
- package/src/dalvikExecutableUnparser/sectionUnparsers.ts +38 -14
- package/src/dalvikExecutableUnparser.test.ts +3 -2
- package/src/dalvikExecutableUnparser.ts +4 -4
- package/src/disjunctionParser.test-d.ts +105 -0
- package/src/disjunctionParser.ts +18 -15
- package/src/elementSwitchParser.test-d.ts +74 -0
- package/src/elementSwitchParser.ts +51 -0
- package/src/exactSequenceParser.test-d.ts +43 -0
- package/src/exactSequenceParser.ts +13 -8
- package/src/fetchCid.ts +2 -76
- package/src/index.test.ts +22 -1
- package/src/index.ts +11 -1
- package/src/inputReader.ts +53 -0
- package/src/java.ts +708 -0
- package/src/javaKeyStoreParser.ts +18 -32
- package/src/javaParser.test.ts +1592 -0
- package/src/javaParser.ts +2640 -0
- package/src/javaScript.ts +36 -0
- package/src/javaScriptParser.ts +57 -0
- package/src/javaScriptUnparser.test.ts +37 -0
- package/src/javaScriptUnparser.ts +7 -0
- package/src/javaUnparser.test.ts +37 -0
- package/src/javaUnparser.ts +640 -0
- package/src/jsonParser.ts +6 -27
- package/src/lookaheadParser.ts +2 -6
- package/src/negativeLookaheadParser.ts +1 -3
- package/src/nonEmptyArrayParser.ts +1 -3
- package/src/objectParser.test-d.ts +152 -0
- package/src/objectParser.test.ts +71 -0
- package/src/objectParser.ts +69 -0
- package/src/optionalParser.ts +1 -3
- package/src/parser.test.ts +151 -4
- package/src/parser.ts +11 -1
- package/src/parserConsumedSequenceParser.ts +2 -4
- package/src/parserContext.ts +26 -11
- package/src/parserError.ts +17 -3
- package/src/predicateElementParser.ts +22 -0
- package/src/regexpParser.test.ts +78 -0
- package/src/regexpParser.ts +35 -3
- package/src/regularExpressionParser.ts +36 -37
- package/src/separatedArrayParser.ts +1 -3
- package/src/separatedNonEmptyArrayParser.ts +1 -3
- package/src/sliceBoundedParser.test.ts +2 -2
- package/src/sliceBoundedParser.ts +15 -19
- package/src/smaliParser.test.ts +64 -0
- package/src/smaliParser.test.ts.md +12 -12
- package/src/smaliParser.test.ts.snap +0 -0
- package/src/smaliParser.ts +246 -534
- package/src/stringEscapes.ts +253 -0
- package/src/symbolicExpression.ts +17 -0
- package/src/symbolicExpressionParser.test.ts +466 -0
- package/src/symbolicExpressionParser.ts +190 -0
- package/src/terminatedArrayParser.test.ts +9 -6
- package/src/terminatedArrayParser.ts +25 -29
- package/src/tupleParser.ts +21 -18
- package/src/unionParser.test-d.ts +105 -0
- package/src/unionParser.test.ts +18 -17
- package/src/unionParser.ts +28 -16
- package/src/zig.ts +411 -0
- package/src/zigParser.test.ts +1693 -0
- package/src/zigParser.ts +1745 -0
- package/src/zigUnparser.test.ts +37 -0
- package/src/zigUnparser.ts +615 -0
- package/src/zipParser.ts +20 -56
- package/src/zipUnparser.test.ts +1 -1
- package/src/zipUnparser.ts +22 -7
- package/tsconfig.json +2 -2
- package/build/exactElementSwitchParser.d.ts +0 -3
- package/src/exactElementSwitchParser.ts +0 -41
package/build/bashParser.js
CHANGED
|
@@ -1,117 +1,368 @@
|
|
|
1
1
|
import { setParserName } from './parser.js';
|
|
2
2
|
import { createExactSequenceParser } from './exactSequenceParser.js';
|
|
3
|
+
import { createElementParser } from './elementParser.js';
|
|
4
|
+
import { createPredicateElementParser } from './predicateElementParser.js';
|
|
5
|
+
import { createNegativeLookaheadParser } from './negativeLookaheadParser.js';
|
|
6
|
+
import { createLookaheadParser } from './lookaheadParser.js';
|
|
3
7
|
import { promiseCompose } from './promiseCompose.js';
|
|
4
8
|
import { createTupleParser } from './tupleParser.js';
|
|
5
9
|
import { createDisjunctionParser } from './disjunctionParser.js';
|
|
6
10
|
import { createArrayParser } from './arrayParser.js';
|
|
7
11
|
import { createParserAccessorParser } from './parserAccessorParser.js';
|
|
8
12
|
import { createOptionalParser } from './optionalParser.js';
|
|
9
|
-
import { createRegExpParser } from './regexpParser.js';
|
|
10
13
|
import { createNonEmptyArrayParser } from './nonEmptyArrayParser.js';
|
|
11
14
|
import { createSeparatedNonEmptyArrayParser } from './separatedNonEmptyArrayParser.js';
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
import { createObjectParser } from './objectParser.js';
|
|
16
|
+
// Character predicates
|
|
17
|
+
function isDigit(ch) {
|
|
18
|
+
return ch >= '0' && ch <= '9';
|
|
19
|
+
}
|
|
20
|
+
function isLetter(ch) {
|
|
21
|
+
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
|
|
22
|
+
}
|
|
23
|
+
function isIdentStart(ch) {
|
|
24
|
+
return isLetter(ch) || ch === '_';
|
|
25
|
+
}
|
|
26
|
+
function isIdentChar(ch) {
|
|
27
|
+
return isIdentStart(ch) || isDigit(ch);
|
|
28
|
+
}
|
|
29
|
+
// Whitespace (spaces, tabs, and line continuations - not bare newlines which are significant)
|
|
30
|
+
const bashInlineWhitespaceUnitParser = createDisjunctionParser([
|
|
31
|
+
promiseCompose(createExactSequenceParser(' '), () => ' '),
|
|
32
|
+
promiseCompose(createExactSequenceParser('\t'), () => '\t'),
|
|
33
|
+
promiseCompose(createExactSequenceParser('\\\n'), () => '\\\n'),
|
|
34
|
+
]);
|
|
35
|
+
const bashInlineWhitespaceParser = promiseCompose(createNonEmptyArrayParser(bashInlineWhitespaceUnitParser), parts => parts.join(''));
|
|
36
|
+
const bashOptionalInlineWhitespaceParser = promiseCompose(createArrayParser(bashInlineWhitespaceUnitParser), parts => parts.join(''));
|
|
17
37
|
// Word characters (unquoted, no special chars)
|
|
18
|
-
// Note: {} are excluded so brace groups are parsed correctly
|
|
19
|
-
|
|
38
|
+
// Note: {} and # are excluded from the first character so brace groups and comments are parsed correctly,
|
|
39
|
+
// but allowed as continuation characters for mid-word braces (e.g., file.{c,h}, foo}bar) and hash (foo#bar)
|
|
40
|
+
const bashSpecialCharParser = createDisjunctionParser([...' \t\n|&;<>()$`"\'\\'].map(ch => createExactSequenceParser(ch)));
|
|
41
|
+
const bashWordStartExcludeParser = createDisjunctionParser([
|
|
42
|
+
bashSpecialCharParser,
|
|
43
|
+
createExactSequenceParser('{'),
|
|
44
|
+
createExactSequenceParser('}'),
|
|
45
|
+
createExactSequenceParser('#'),
|
|
46
|
+
]);
|
|
47
|
+
const bashUnquotedWordStartCharParser = promiseCompose(createTupleParser([
|
|
48
|
+
createNegativeLookaheadParser(bashWordStartExcludeParser),
|
|
49
|
+
createElementParser(),
|
|
50
|
+
]), ([, ch]) => ch);
|
|
51
|
+
const bashUnquotedWordContinueCharParser = promiseCompose(createTupleParser([
|
|
52
|
+
createNegativeLookaheadParser(bashSpecialCharParser),
|
|
53
|
+
createElementParser(),
|
|
54
|
+
]), ([, ch]) => ch);
|
|
55
|
+
const bashUnquotedWordCharsParser = promiseCompose(createTupleParser([
|
|
56
|
+
bashUnquotedWordStartCharParser,
|
|
57
|
+
createArrayParser(bashUnquotedWordContinueCharParser),
|
|
58
|
+
]), ([first, rest]) => first + rest.join(''));
|
|
59
|
+
// Consume characters until a given terminator, returning the accumulated string
|
|
60
|
+
function createUntilCharParser(terminator) {
|
|
61
|
+
return promiseCompose(createArrayParser(promiseCompose(createTupleParser([
|
|
62
|
+
createNegativeLookaheadParser(createExactSequenceParser(terminator)),
|
|
63
|
+
createElementParser(),
|
|
64
|
+
]), ([, ch]) => ch)), chars => chars.join(''));
|
|
65
|
+
}
|
|
20
66
|
// Single quoted string: '...'
|
|
21
|
-
const bashSingleQuotedParser =
|
|
22
|
-
createExactSequenceParser("'"),
|
|
23
|
-
promiseCompose(createRegExpParser(/[^']*/), match => match[0]),
|
|
24
|
-
createExactSequenceParser("'"),
|
|
25
|
-
]), ([, value]) => ({
|
|
67
|
+
const bashSingleQuotedParser = createObjectParser({
|
|
26
68
|
type: 'singleQuoted',
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
69
|
+
_open: createExactSequenceParser("'"),
|
|
70
|
+
value: createUntilCharParser("'"),
|
|
71
|
+
_close: createExactSequenceParser("'"),
|
|
72
|
+
});
|
|
73
|
+
// Variable name: identifiers, positional params ($0, $1...), or special params ($@, $*, $#, $?, $$, $!, $-)
|
|
74
|
+
const bashSpecialParams = new Set(['@', '*', '#', '?', '$', '!', '-']);
|
|
75
|
+
const bashIdentifierParser = promiseCompose(createTupleParser([
|
|
76
|
+
createPredicateElementParser(isIdentStart),
|
|
77
|
+
createArrayParser(createPredicateElementParser(isIdentChar)),
|
|
78
|
+
]), ([first, rest]) => first + rest.join(''));
|
|
79
|
+
const bashDigitsParser = promiseCompose(createNonEmptyArrayParser(createPredicateElementParser(isDigit)), chars => chars.join(''));
|
|
80
|
+
const bashSpecialParamParser = createPredicateElementParser(ch => bashSpecialParams.has(ch));
|
|
81
|
+
const bashVariableNameParser = createDisjunctionParser([
|
|
82
|
+
bashIdentifierParser,
|
|
83
|
+
bashDigitsParser,
|
|
84
|
+
bashSpecialParamParser,
|
|
85
|
+
]);
|
|
31
86
|
// Simple variable: $var
|
|
32
|
-
const bashSimpleVariableParser =
|
|
33
|
-
createExactSequenceParser('$'),
|
|
34
|
-
bashVariableNameParser,
|
|
35
|
-
]), ([, name]) => ({
|
|
87
|
+
const bashSimpleVariableParser = createObjectParser({
|
|
36
88
|
type: 'variable',
|
|
37
|
-
|
|
38
|
-
|
|
89
|
+
_dollar: createExactSequenceParser('$'),
|
|
90
|
+
name: bashVariableNameParser,
|
|
91
|
+
});
|
|
39
92
|
// Command substitution: $(...)
|
|
40
|
-
const bashCommandSubstitutionParser =
|
|
41
|
-
createExactSequenceParser('$('),
|
|
42
|
-
bashOptionalInlineWhitespaceParser,
|
|
43
|
-
createParserAccessorParser(() => bashCommandParser),
|
|
44
|
-
bashOptionalInlineWhitespaceParser,
|
|
45
|
-
createExactSequenceParser(')'),
|
|
46
|
-
]), ([, , command]) => ({
|
|
93
|
+
const bashCommandSubstitutionParser = createObjectParser({
|
|
47
94
|
type: 'commandSubstitution',
|
|
48
|
-
|
|
49
|
-
|
|
95
|
+
_open: createExactSequenceParser('$('),
|
|
96
|
+
_ws1: bashOptionalInlineWhitespaceParser,
|
|
97
|
+
command: createParserAccessorParser(() => bashCommandParser),
|
|
98
|
+
_ws2: bashOptionalInlineWhitespaceParser,
|
|
99
|
+
_close: createExactSequenceParser(')'),
|
|
100
|
+
});
|
|
50
101
|
// Backtick substitution: `...`
|
|
51
|
-
const bashBacktickSubstitutionParser =
|
|
52
|
-
createExactSequenceParser('`'),
|
|
53
|
-
createParserAccessorParser(() => bashCommandParser),
|
|
54
|
-
createExactSequenceParser('`'),
|
|
55
|
-
]), ([, command]) => ({
|
|
102
|
+
const bashBacktickSubstitutionParser = createObjectParser({
|
|
56
103
|
type: 'backtickSubstitution',
|
|
57
|
-
|
|
58
|
-
|
|
104
|
+
_open: createExactSequenceParser('`'),
|
|
105
|
+
command: createParserAccessorParser(() => bashCommandParser),
|
|
106
|
+
_close: createExactSequenceParser('`'),
|
|
107
|
+
});
|
|
108
|
+
// Word characters for use inside ${...} operands (} excluded from continuation to not consume the closing brace)
|
|
109
|
+
const bashBracedVarContinueExcludeParser = createDisjunctionParser([
|
|
110
|
+
bashSpecialCharParser,
|
|
111
|
+
createExactSequenceParser('{'),
|
|
112
|
+
createExactSequenceParser('}'),
|
|
113
|
+
]);
|
|
114
|
+
const bashBracedVarUnquotedWordCharsParser = promiseCompose(createTupleParser([
|
|
115
|
+
bashUnquotedWordStartCharParser,
|
|
116
|
+
createArrayParser(promiseCompose(createTupleParser([
|
|
117
|
+
createNegativeLookaheadParser(bashBracedVarContinueExcludeParser),
|
|
118
|
+
createElementParser(),
|
|
119
|
+
]), ([, ch]) => ch)),
|
|
120
|
+
]), ([first, rest]) => first + rest.join(''));
|
|
121
|
+
const bashBracedVarLiteralWordPartParser = createObjectParser({
|
|
122
|
+
type: 'literal',
|
|
123
|
+
value: bashBracedVarUnquotedWordCharsParser,
|
|
124
|
+
});
|
|
125
|
+
// Braced variable expansion: ${VAR} or ${VAR:-default}
|
|
126
|
+
const bashBracedVariableParser = createObjectParser({
|
|
127
|
+
type: 'variableBraced',
|
|
128
|
+
_open: createExactSequenceParser('${'),
|
|
129
|
+
name: bashVariableNameParser,
|
|
130
|
+
operator: createOptionalParser(createDisjunctionParser([
|
|
131
|
+
promiseCompose(createExactSequenceParser(':-'), () => ':-'),
|
|
132
|
+
promiseCompose(createExactSequenceParser(':='), () => ':='),
|
|
133
|
+
promiseCompose(createExactSequenceParser(':+'), () => ':+'),
|
|
134
|
+
promiseCompose(createExactSequenceParser(':?'), () => ':?'),
|
|
135
|
+
promiseCompose(createExactSequenceParser('##'), () => '##'),
|
|
136
|
+
promiseCompose(createExactSequenceParser('%%'), () => '%%'),
|
|
137
|
+
promiseCompose(createExactSequenceParser('-'), () => '-'),
|
|
138
|
+
promiseCompose(createExactSequenceParser('='), () => '='),
|
|
139
|
+
promiseCompose(createExactSequenceParser('+'), () => '+'),
|
|
140
|
+
promiseCompose(createExactSequenceParser('?'), () => '?'),
|
|
141
|
+
promiseCompose(createExactSequenceParser('#'), () => '#'),
|
|
142
|
+
promiseCompose(createExactSequenceParser('%'), () => '%'),
|
|
143
|
+
])),
|
|
144
|
+
operand: createOptionalParser(createParserAccessorParser(() => bashBracedVarWordParser)),
|
|
145
|
+
_close: createExactSequenceParser('}'),
|
|
146
|
+
});
|
|
147
|
+
// Arithmetic expansion: $((expression)) - handles nested parentheses
|
|
148
|
+
const bashArithmeticExpressionParser = async (parserContext) => {
|
|
149
|
+
let result = '';
|
|
150
|
+
let depth = 0;
|
|
151
|
+
for (;;) {
|
|
152
|
+
const ch = await parserContext.peek(0);
|
|
153
|
+
if (ch === undefined) {
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
if (ch === '(') {
|
|
157
|
+
depth++;
|
|
158
|
+
result += ch;
|
|
159
|
+
parserContext.skip(1);
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
if (ch === ')') {
|
|
163
|
+
if (depth > 0) {
|
|
164
|
+
depth--;
|
|
165
|
+
result += ch;
|
|
166
|
+
parserContext.skip(1);
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// At depth 0, a ')' means we've hit the closing '))' of $((
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
result += ch;
|
|
173
|
+
parserContext.skip(1);
|
|
174
|
+
}
|
|
175
|
+
return result;
|
|
176
|
+
};
|
|
177
|
+
const bashArithmeticExpansionParser = createObjectParser({
|
|
178
|
+
type: 'arithmeticExpansion',
|
|
179
|
+
_open: createExactSequenceParser('$(('),
|
|
180
|
+
expression: bashArithmeticExpressionParser,
|
|
181
|
+
_close: createExactSequenceParser('))'),
|
|
182
|
+
});
|
|
183
|
+
// ANSI-C quoting: $'...' - content can include \' escapes
|
|
184
|
+
// Each unit is either a backslash-escape pair or a non-quote character
|
|
185
|
+
const bashAnsiCContentUnitParser = createDisjunctionParser([
|
|
186
|
+
// Backslash escape: \x (any char after backslash)
|
|
187
|
+
promiseCompose(createTupleParser([
|
|
188
|
+
createExactSequenceParser('\\'),
|
|
189
|
+
createElementParser(),
|
|
190
|
+
]), ([bs, ch]) => bs + ch),
|
|
191
|
+
// Any character that isn't ' (and isn't \ which is handled above)
|
|
192
|
+
promiseCompose(createTupleParser([
|
|
193
|
+
createNegativeLookaheadParser(createExactSequenceParser("'")),
|
|
194
|
+
createElementParser(),
|
|
195
|
+
]), ([, ch]) => ch),
|
|
196
|
+
]);
|
|
197
|
+
const bashAnsiCContentParser = promiseCompose(createArrayParser(bashAnsiCContentUnitParser), parts => parts.join(''));
|
|
198
|
+
const bashAnsiCQuotedParser = createObjectParser({
|
|
199
|
+
type: 'singleQuoted',
|
|
200
|
+
_prefix: createExactSequenceParser('$'),
|
|
201
|
+
_open: createExactSequenceParser("'"),
|
|
202
|
+
value: bashAnsiCContentParser,
|
|
203
|
+
_close: createExactSequenceParser("'"),
|
|
204
|
+
});
|
|
205
|
+
// Process substitution: <(cmd) or >(cmd)
|
|
206
|
+
const bashProcessSubstitutionDirectionParser = promiseCompose(createTupleParser([
|
|
207
|
+
createDisjunctionParser([
|
|
208
|
+
createExactSequenceParser('<'),
|
|
209
|
+
createExactSequenceParser('>'),
|
|
210
|
+
]),
|
|
211
|
+
createLookaheadParser(createExactSequenceParser('(')),
|
|
212
|
+
]), ([dir]) => dir);
|
|
213
|
+
const bashProcessSubstitutionParser = createObjectParser({
|
|
214
|
+
type: 'processSubstitution',
|
|
215
|
+
direction: bashProcessSubstitutionDirectionParser,
|
|
216
|
+
_open: createExactSequenceParser('('),
|
|
217
|
+
_ws1: bashOptionalInlineWhitespaceParser,
|
|
218
|
+
command: createParserAccessorParser(() => bashCommandParser),
|
|
219
|
+
_ws2: bashOptionalInlineWhitespaceParser,
|
|
220
|
+
_close: createExactSequenceParser(')'),
|
|
221
|
+
});
|
|
222
|
+
// Escape sequences in double quotes: \\ \$ \` \" \! \newline
|
|
223
|
+
const bashDoubleQuotedEscapeCharParser = createDisjunctionParser([
|
|
224
|
+
createExactSequenceParser('\\'),
|
|
225
|
+
createExactSequenceParser('$'),
|
|
226
|
+
createExactSequenceParser('`'),
|
|
227
|
+
createExactSequenceParser('"'),
|
|
228
|
+
createExactSequenceParser('!'),
|
|
229
|
+
createExactSequenceParser('\n'),
|
|
230
|
+
]);
|
|
231
|
+
const bashDoubleQuotedEscapeParser = promiseCompose(createTupleParser([
|
|
232
|
+
createExactSequenceParser('\\'),
|
|
233
|
+
bashDoubleQuotedEscapeCharParser,
|
|
234
|
+
]), ([, ch]) => ({ type: 'literal', value: ch }));
|
|
235
|
+
// Literal text inside double quotes (no special chars)
|
|
236
|
+
const bashDoubleQuotedLiteralCharParser = promiseCompose(createTupleParser([
|
|
237
|
+
createNegativeLookaheadParser(createDisjunctionParser([
|
|
238
|
+
createExactSequenceParser('$'),
|
|
239
|
+
createExactSequenceParser('`'),
|
|
240
|
+
createExactSequenceParser('"'),
|
|
241
|
+
createExactSequenceParser('\\'),
|
|
242
|
+
])),
|
|
243
|
+
createElementParser(),
|
|
244
|
+
]), ([, ch]) => ch);
|
|
245
|
+
const bashDoubleQuotedLiteralParser = promiseCompose(createNonEmptyArrayParser(bashDoubleQuotedLiteralCharParser), chars => ({ type: 'literal', value: chars.join('') }));
|
|
246
|
+
// Bare $ not followed by a valid expansion start
|
|
247
|
+
const bashBareDollarParser = promiseCompose(createExactSequenceParser('$'), () => ({ type: 'literal', value: '$' }));
|
|
248
|
+
// Bare \ not followed by a recognized escape character
|
|
249
|
+
const bashBareBackslashParser = promiseCompose(createExactSequenceParser('\\'), () => ({ type: 'literal', value: '\\' }));
|
|
59
250
|
// Double quoted string parts (inside "...")
|
|
60
251
|
const bashDoubleQuotedPartParser = createDisjunctionParser([
|
|
252
|
+
bashBracedVariableParser,
|
|
253
|
+
bashArithmeticExpansionParser,
|
|
61
254
|
bashSimpleVariableParser,
|
|
62
255
|
bashCommandSubstitutionParser,
|
|
63
256
|
bashBacktickSubstitutionParser,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
})),
|
|
69
|
-
// Literal text (no special chars)
|
|
70
|
-
promiseCompose(createRegExpParser(/[^$`"\\]+/), match => ({
|
|
71
|
-
type: 'literal',
|
|
72
|
-
value: match[0],
|
|
73
|
-
})),
|
|
257
|
+
bashDoubleQuotedEscapeParser,
|
|
258
|
+
bashDoubleQuotedLiteralParser,
|
|
259
|
+
bashBareDollarParser,
|
|
260
|
+
bashBareBackslashParser,
|
|
74
261
|
]);
|
|
75
262
|
// Double quoted string: "..."
|
|
76
|
-
const bashDoubleQuotedParser =
|
|
77
|
-
createExactSequenceParser('"'),
|
|
78
|
-
createArrayParser(bashDoubleQuotedPartParser),
|
|
79
|
-
createExactSequenceParser('"'),
|
|
80
|
-
]), ([, parts]) => ({
|
|
263
|
+
const bashDoubleQuotedParser = createObjectParser({
|
|
81
264
|
type: 'doubleQuoted',
|
|
82
|
-
|
|
83
|
-
|
|
265
|
+
_open: createExactSequenceParser('"'),
|
|
266
|
+
parts: createArrayParser(bashDoubleQuotedPartParser),
|
|
267
|
+
_close: createExactSequenceParser('"'),
|
|
268
|
+
});
|
|
84
269
|
// Literal word part (unquoted)
|
|
85
|
-
const bashLiteralWordPartParser =
|
|
270
|
+
const bashLiteralWordPartParser = createObjectParser({
|
|
271
|
+
type: 'literal',
|
|
272
|
+
value: bashUnquotedWordCharsParser,
|
|
273
|
+
});
|
|
274
|
+
// Bare {} treated as a literal word (e.g., find -exec cmd {} \;)
|
|
275
|
+
const bashBraceWordPartParser = promiseCompose(createExactSequenceParser('{}'), () => ({
|
|
86
276
|
type: 'literal',
|
|
87
|
-
value,
|
|
277
|
+
value: '{}',
|
|
88
278
|
}));
|
|
89
|
-
//
|
|
90
|
-
|
|
279
|
+
// Bare { treated as a literal word part (e.g., echo {, echo {.})
|
|
280
|
+
// Note: } is NOT included here because it would break brace group closing
|
|
281
|
+
const bashOpenBraceWordPartParser = promiseCompose(createExactSequenceParser('{'), () => ({
|
|
91
282
|
type: 'literal',
|
|
92
|
-
value:
|
|
283
|
+
value: '{',
|
|
93
284
|
}));
|
|
94
|
-
//
|
|
285
|
+
// Bare } treated as a literal word part (e.g., echo }, echo }hello)
|
|
286
|
+
const bashCloseBraceWordPartParser = promiseCompose(createExactSequenceParser('}'), () => ({
|
|
287
|
+
type: 'literal',
|
|
288
|
+
value: '}',
|
|
289
|
+
}));
|
|
290
|
+
// Escape sequence outside quotes: backslash followed by any character
|
|
291
|
+
const bashEscapeParser = promiseCompose(createTupleParser([
|
|
292
|
+
createExactSequenceParser('\\'),
|
|
293
|
+
createElementParser(),
|
|
294
|
+
]), ([, ch]) => ({ type: 'literal', value: ch }));
|
|
295
|
+
// Word part for use inside ${...} operands (uses literal parser that excludes } from continuation)
|
|
296
|
+
const bashBracedVarWordPartParser = createDisjunctionParser([
|
|
297
|
+
bashAnsiCQuotedParser,
|
|
298
|
+
bashSingleQuotedParser,
|
|
299
|
+
bashDoubleQuotedParser,
|
|
300
|
+
bashBracedVariableParser,
|
|
301
|
+
bashArithmeticExpansionParser,
|
|
302
|
+
bashCommandSubstitutionParser,
|
|
303
|
+
bashBacktickSubstitutionParser,
|
|
304
|
+
bashSimpleVariableParser,
|
|
305
|
+
bashEscapeParser,
|
|
306
|
+
bashBracedVarLiteralWordPartParser,
|
|
307
|
+
bashBareDollarParser,
|
|
308
|
+
]);
|
|
309
|
+
const bashBracedVarWordParser = createObjectParser({
|
|
310
|
+
parts: createNonEmptyArrayParser(bashBracedVarWordPartParser),
|
|
311
|
+
});
|
|
312
|
+
// Word part (any part of a word, } excluded from first position so brace groups work)
|
|
95
313
|
const bashWordPartParser = createDisjunctionParser([
|
|
314
|
+
bashAnsiCQuotedParser,
|
|
315
|
+
bashSingleQuotedParser,
|
|
316
|
+
bashDoubleQuotedParser,
|
|
317
|
+
bashBracedVariableParser,
|
|
318
|
+
bashArithmeticExpansionParser,
|
|
319
|
+
bashCommandSubstitutionParser,
|
|
320
|
+
bashBacktickSubstitutionParser,
|
|
321
|
+
bashSimpleVariableParser,
|
|
322
|
+
bashProcessSubstitutionParser,
|
|
323
|
+
bashEscapeParser,
|
|
324
|
+
bashBraceWordPartParser,
|
|
325
|
+
bashOpenBraceWordPartParser,
|
|
326
|
+
bashLiteralWordPartParser,
|
|
327
|
+
bashBareDollarParser,
|
|
328
|
+
]);
|
|
329
|
+
// Word part including } as a starter (for argument positions where } is not reserved)
|
|
330
|
+
const bashArgWordPartParser = createDisjunctionParser([
|
|
331
|
+
bashAnsiCQuotedParser,
|
|
96
332
|
bashSingleQuotedParser,
|
|
97
333
|
bashDoubleQuotedParser,
|
|
334
|
+
bashBracedVariableParser,
|
|
335
|
+
bashArithmeticExpansionParser,
|
|
98
336
|
bashCommandSubstitutionParser,
|
|
99
337
|
bashBacktickSubstitutionParser,
|
|
100
338
|
bashSimpleVariableParser,
|
|
339
|
+
bashProcessSubstitutionParser,
|
|
101
340
|
bashEscapeParser,
|
|
341
|
+
bashBraceWordPartParser,
|
|
342
|
+
bashOpenBraceWordPartParser,
|
|
343
|
+
bashCloseBraceWordPartParser,
|
|
102
344
|
bashLiteralWordPartParser,
|
|
345
|
+
bashBareDollarParser,
|
|
103
346
|
]);
|
|
104
347
|
// Word (sequence of word parts)
|
|
105
|
-
export const bashWordParser =
|
|
348
|
+
export const bashWordParser = createObjectParser({
|
|
349
|
+
parts: createNonEmptyArrayParser(bashWordPartParser),
|
|
350
|
+
});
|
|
351
|
+
// Argument word (allows } as first character)
|
|
352
|
+
const bashArgWordParser = createObjectParser({
|
|
353
|
+
parts: createNonEmptyArrayParser(bashArgWordPartParser),
|
|
354
|
+
});
|
|
106
355
|
setParserName(bashWordParser, 'bashWordParser');
|
|
356
|
+
// Assignment name: identifier followed by =
|
|
357
|
+
const bashAssignmentNameParser = promiseCompose(createTupleParser([
|
|
358
|
+
bashIdentifierParser,
|
|
359
|
+
createExactSequenceParser('='),
|
|
360
|
+
]), ([name]) => name);
|
|
107
361
|
// Assignment: NAME=value or NAME=
|
|
108
|
-
const bashAssignmentParser =
|
|
109
|
-
|
|
110
|
-
createOptionalParser(bashWordParser),
|
|
111
|
-
|
|
112
|
-
name,
|
|
113
|
-
value: value ?? undefined,
|
|
114
|
-
}));
|
|
362
|
+
const bashAssignmentParser = createObjectParser({
|
|
363
|
+
name: bashAssignmentNameParser,
|
|
364
|
+
value: createOptionalParser(bashWordParser),
|
|
365
|
+
});
|
|
115
366
|
// Redirect operators
|
|
116
367
|
const bashRedirectOperatorParser = createDisjunctionParser([
|
|
117
368
|
promiseCompose(createExactSequenceParser('>>'), () => '>>'),
|
|
@@ -123,53 +374,62 @@ const bashRedirectOperatorParser = createDisjunctionParser([
|
|
|
123
374
|
promiseCompose(createExactSequenceParser('<&'), () => '<&'),
|
|
124
375
|
promiseCompose(createExactSequenceParser('<'), () => '<'),
|
|
125
376
|
]);
|
|
377
|
+
// File descriptor number
|
|
378
|
+
const bashFdParser = promiseCompose(bashDigitsParser, digits => Number.parseInt(digits, 10));
|
|
126
379
|
// Redirect: [n]op word
|
|
127
|
-
const bashRedirectParser =
|
|
128
|
-
createOptionalParser(
|
|
129
|
-
bashRedirectOperatorParser,
|
|
130
|
-
bashOptionalInlineWhitespaceParser,
|
|
131
|
-
bashWordParser,
|
|
132
|
-
|
|
133
|
-
fd: fd ?? undefined,
|
|
134
|
-
operator,
|
|
135
|
-
target,
|
|
136
|
-
}));
|
|
380
|
+
const bashRedirectParser = createObjectParser({
|
|
381
|
+
fd: createOptionalParser(bashFdParser),
|
|
382
|
+
operator: bashRedirectOperatorParser,
|
|
383
|
+
_ws: bashOptionalInlineWhitespaceParser,
|
|
384
|
+
target: bashWordParser,
|
|
385
|
+
});
|
|
137
386
|
// Word with optional trailing whitespace - for use in arrays
|
|
138
387
|
const bashWordWithWhitespaceParser = promiseCompose(createTupleParser([
|
|
139
388
|
bashWordParser,
|
|
140
389
|
bashOptionalInlineWhitespaceParser,
|
|
141
390
|
]), ([word]) => word);
|
|
391
|
+
// Arg word (allows }) with optional trailing whitespace
|
|
392
|
+
const bashArgWordWithWhitespaceParser = promiseCompose(createTupleParser([
|
|
393
|
+
bashArgWordParser,
|
|
394
|
+
bashOptionalInlineWhitespaceParser,
|
|
395
|
+
]), ([word]) => word);
|
|
142
396
|
// Redirect with optional trailing whitespace
|
|
143
397
|
const bashRedirectWithWhitespaceParser = promiseCompose(createTupleParser([
|
|
144
398
|
bashRedirectParser,
|
|
145
399
|
bashOptionalInlineWhitespaceParser,
|
|
146
400
|
]), ([redirect]) => redirect);
|
|
147
|
-
// Word or redirect
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
401
|
+
// Word or redirect for argument position (} allowed)
|
|
402
|
+
const bashArgWordOrRedirectParser = createDisjunctionParser([
|
|
403
|
+
createObjectParser({ type: 'redirect', redirect: bashRedirectWithWhitespaceParser }),
|
|
404
|
+
createObjectParser({ type: 'word', word: bashArgWordWithWhitespaceParser }),
|
|
151
405
|
]);
|
|
152
406
|
// Simple command: [assignments] [name] [args] [redirects]
|
|
153
|
-
export const bashSimpleCommandParser =
|
|
154
|
-
//
|
|
155
|
-
createArrayParser(promiseCompose(createTupleParser([
|
|
407
|
+
export const bashSimpleCommandParser = async (parserContext) => {
|
|
408
|
+
// Parse assignments at the start
|
|
409
|
+
const assignmentsParser = createArrayParser(promiseCompose(createTupleParser([
|
|
156
410
|
bashAssignmentParser,
|
|
157
411
|
bashOptionalInlineWhitespaceParser,
|
|
158
|
-
]), ([assignment]) => assignment))
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
412
|
+
]), ([assignment]) => assignment));
|
|
413
|
+
const assignments = await assignmentsParser(parserContext);
|
|
414
|
+
// Parse leading redirects before command name
|
|
415
|
+
const leadingRedirectsParser = createArrayParser(bashRedirectWithWhitespaceParser);
|
|
416
|
+
const leadingRedirects = await leadingRedirectsParser(parserContext);
|
|
417
|
+
// Parse command name (} not allowed here, so brace group closing works)
|
|
418
|
+
const name = await createOptionalParser(bashWordWithWhitespaceParser)(parserContext);
|
|
419
|
+
// Only parse args if we have a command name
|
|
420
|
+
const args = [];
|
|
421
|
+
const redirects = [...leadingRedirects];
|
|
422
|
+
if (name !== undefined) {
|
|
423
|
+
const argItems = await createArrayParser(bashArgWordOrRedirectParser)(parserContext);
|
|
424
|
+
for (const item of argItems) {
|
|
425
|
+
if (item.type === 'word') {
|
|
426
|
+
args.push(item.word);
|
|
427
|
+
}
|
|
428
|
+
else {
|
|
429
|
+
redirects.push(item.redirect);
|
|
430
|
+
}
|
|
170
431
|
}
|
|
171
432
|
}
|
|
172
|
-
const [name, ...args] = words;
|
|
173
433
|
return {
|
|
174
434
|
type: 'simple',
|
|
175
435
|
name,
|
|
@@ -177,33 +437,29 @@ export const bashSimpleCommandParser = promiseCompose(createTupleParser([
|
|
|
177
437
|
redirects,
|
|
178
438
|
assignments,
|
|
179
439
|
};
|
|
180
|
-
}
|
|
440
|
+
};
|
|
181
441
|
setParserName(bashSimpleCommandParser, 'bashSimpleCommandParser');
|
|
182
442
|
// Subshell: ( command )
|
|
183
|
-
const bashSubshellParser =
|
|
184
|
-
createExactSequenceParser('('),
|
|
185
|
-
bashOptionalInlineWhitespaceParser,
|
|
186
|
-
createParserAccessorParser(() => bashCommandParser),
|
|
187
|
-
bashOptionalInlineWhitespaceParser,
|
|
188
|
-
createExactSequenceParser(')'),
|
|
189
|
-
]), ([, , body]) => ({
|
|
443
|
+
const bashSubshellParser = createObjectParser({
|
|
190
444
|
type: 'subshell',
|
|
191
|
-
|
|
192
|
-
|
|
445
|
+
_open: createExactSequenceParser('('),
|
|
446
|
+
_ws1: bashOptionalInlineWhitespaceParser,
|
|
447
|
+
body: createParserAccessorParser(() => bashCommandParser),
|
|
448
|
+
_ws2: bashOptionalInlineWhitespaceParser,
|
|
449
|
+
_close: createExactSequenceParser(')'),
|
|
450
|
+
});
|
|
193
451
|
setParserName(bashSubshellParser, 'bashSubshellParser');
|
|
194
452
|
// Brace group: { command; }
|
|
195
|
-
const bashBraceGroupParser =
|
|
196
|
-
createExactSequenceParser('{'),
|
|
197
|
-
bashInlineWhitespaceParser,
|
|
198
|
-
createParserAccessorParser(() => bashCommandParser),
|
|
199
|
-
bashOptionalInlineWhitespaceParser,
|
|
200
|
-
createOptionalParser(createExactSequenceParser(';')),
|
|
201
|
-
bashOptionalInlineWhitespaceParser,
|
|
202
|
-
createExactSequenceParser('}'),
|
|
203
|
-
]), ([, , body]) => ({
|
|
453
|
+
const bashBraceGroupParser = createObjectParser({
|
|
204
454
|
type: 'braceGroup',
|
|
205
|
-
|
|
206
|
-
|
|
455
|
+
_open: createExactSequenceParser('{'),
|
|
456
|
+
_ws1: bashInlineWhitespaceParser,
|
|
457
|
+
body: createParserAccessorParser(() => bashCommandParser),
|
|
458
|
+
_ws2: bashOptionalInlineWhitespaceParser,
|
|
459
|
+
_semi: createOptionalParser(createExactSequenceParser(';')),
|
|
460
|
+
_ws3: bashOptionalInlineWhitespaceParser,
|
|
461
|
+
_close: createExactSequenceParser('}'),
|
|
462
|
+
});
|
|
207
463
|
setParserName(bashBraceGroupParser, 'bashBraceGroupParser');
|
|
208
464
|
// Command unit: simple command, subshell, or brace group
|
|
209
465
|
const bashCommandUnitParser = createDisjunctionParser([
|
|
@@ -213,7 +469,10 @@ const bashCommandUnitParser = createDisjunctionParser([
|
|
|
213
469
|
]);
|
|
214
470
|
setParserName(bashCommandUnitParser, 'bashCommandUnitParser');
|
|
215
471
|
// Single pipe (not ||) - matches | only when not followed by another |
|
|
216
|
-
const bashSinglePipeParser = promiseCompose(
|
|
472
|
+
const bashSinglePipeParser = promiseCompose(createTupleParser([
|
|
473
|
+
createExactSequenceParser('|'),
|
|
474
|
+
createNegativeLookaheadParser(createExactSequenceParser('|')),
|
|
475
|
+
]), () => '|');
|
|
217
476
|
// Pipeline: [!] cmd [| cmd]...
|
|
218
477
|
const bashPipelineParser = promiseCompose(createTupleParser([
|
|
219
478
|
createOptionalParser(promiseCompose(createTupleParser([
|
|
@@ -231,23 +490,45 @@ const bashPipelineParser = promiseCompose(createTupleParser([
|
|
|
231
490
|
commands,
|
|
232
491
|
}));
|
|
233
492
|
setParserName(bashPipelineParser, 'bashPipelineParser');
|
|
493
|
+
// Non-newline character
|
|
494
|
+
const bashNonNewlineCharParser = promiseCompose(createTupleParser([
|
|
495
|
+
createNegativeLookaheadParser(createExactSequenceParser('\n')),
|
|
496
|
+
createElementParser(),
|
|
497
|
+
]), ([, ch]) => ch);
|
|
498
|
+
// Comment: # through end of line (not consuming the newline)
|
|
499
|
+
const bashCommentParser = promiseCompose(createTupleParser([
|
|
500
|
+
createExactSequenceParser('#'),
|
|
501
|
+
createArrayParser(bashNonNewlineCharParser),
|
|
502
|
+
]), ([hash, chars]) => hash + chars.join(''));
|
|
503
|
+
// Blank line filler: whitespace, newlines, and comments
|
|
504
|
+
const bashBlankLineFillerParser = promiseCompose(createArrayParser(createDisjunctionParser([
|
|
505
|
+
bashInlineWhitespaceUnitParser,
|
|
506
|
+
promiseCompose(createExactSequenceParser('\n'), () => '\n'),
|
|
507
|
+
bashCommentParser,
|
|
508
|
+
])), () => { });
|
|
509
|
+
// Newline separator: consumes a newline plus any following blank lines, comments, and whitespace
|
|
510
|
+
// This allows multi-line scripts with blank lines and mid-script comments
|
|
511
|
+
const bashNewlineSeparatorParser = promiseCompose(createTupleParser([
|
|
512
|
+
createExactSequenceParser('\n'),
|
|
513
|
+
bashBlankLineFillerParser,
|
|
514
|
+
]), () => '\n');
|
|
234
515
|
// Command list separator
|
|
235
516
|
const bashListSeparatorParser = createDisjunctionParser([
|
|
236
517
|
promiseCompose(createExactSequenceParser('&&'), () => '&&'),
|
|
237
518
|
promiseCompose(createExactSequenceParser('||'), () => '||'),
|
|
238
519
|
promiseCompose(createExactSequenceParser(';'), () => ';'),
|
|
239
520
|
promiseCompose(createExactSequenceParser('&'), () => '&'),
|
|
240
|
-
|
|
521
|
+
bashNewlineSeparatorParser,
|
|
241
522
|
]);
|
|
242
523
|
// Command list: pipeline [sep pipeline]...
|
|
243
524
|
const bashCommandListParser = promiseCompose(createTupleParser([
|
|
244
525
|
bashPipelineParser,
|
|
245
|
-
createArrayParser(
|
|
246
|
-
bashOptionalInlineWhitespaceParser,
|
|
247
|
-
bashListSeparatorParser,
|
|
248
|
-
bashOptionalInlineWhitespaceParser,
|
|
249
|
-
bashPipelineParser,
|
|
250
|
-
|
|
526
|
+
createArrayParser(createObjectParser({
|
|
527
|
+
_ws1: bashOptionalInlineWhitespaceParser,
|
|
528
|
+
separator: bashListSeparatorParser,
|
|
529
|
+
_ws2: bashOptionalInlineWhitespaceParser,
|
|
530
|
+
pipeline: bashPipelineParser,
|
|
531
|
+
})),
|
|
251
532
|
createOptionalParser(promiseCompose(createTupleParser([
|
|
252
533
|
bashOptionalInlineWhitespaceParser,
|
|
253
534
|
bashListSeparatorParser,
|
|
@@ -285,10 +566,12 @@ setParserName(bashCommandListParser, 'bashCommandListParser');
|
|
|
285
566
|
// Top-level command parser
|
|
286
567
|
export const bashCommandParser = bashCommandListParser;
|
|
287
568
|
setParserName(bashCommandParser, 'bashCommandParser');
|
|
288
|
-
//
|
|
569
|
+
// Trailing whitespace/comments/blank lines at end of script
|
|
570
|
+
const bashTrailingWhitespaceAndCommentsParser = promiseCompose(bashBlankLineFillerParser, () => undefined);
|
|
571
|
+
// Script parser (handles leading/trailing whitespace and comments)
|
|
289
572
|
export const bashScriptParser = promiseCompose(createTupleParser([
|
|
290
573
|
bashOptionalInlineWhitespaceParser,
|
|
291
574
|
bashCommandParser,
|
|
292
|
-
|
|
575
|
+
bashTrailingWhitespaceAndCommentsParser,
|
|
293
576
|
]), ([, command]) => command);
|
|
294
577
|
setParserName(bashScriptParser, 'bashScriptParser');
|