shell-dsl 0.0.6 → 0.0.8
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/README.md +3 -1
- package/dist/cjs/index.cjs +14 -4
- package/dist/cjs/index.cjs.map +3 -3
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/src/commands/awk/awk.cjs +276 -0
- package/dist/cjs/src/commands/awk/awk.cjs.map +10 -0
- package/dist/cjs/src/commands/break-continue/break-continue.cjs +56 -0
- package/dist/cjs/src/commands/break-continue/break-continue.cjs.map +10 -0
- package/dist/cjs/src/commands/cat/cat.cjs +57 -0
- package/dist/cjs/src/commands/cat/cat.cjs.map +10 -0
- package/dist/cjs/src/commands/cp/cp.cjs +126 -0
- package/dist/cjs/src/commands/cp/cp.cjs.map +10 -0
- package/dist/cjs/src/commands/echo/echo.cjs +50 -0
- package/dist/cjs/src/commands/echo/echo.cjs.map +10 -0
- package/dist/cjs/src/commands/find/find.cjs +251 -0
- package/dist/cjs/src/commands/find/find.cjs.map +10 -0
- package/dist/cjs/src/commands/grep/grep.cjs +510 -0
- package/dist/cjs/src/commands/grep/grep.cjs.map +10 -0
- package/dist/cjs/src/commands/head/head.cjs +95 -0
- package/dist/cjs/src/commands/head/head.cjs.map +10 -0
- package/dist/cjs/src/commands/index.cjs +136 -0
- package/dist/cjs/src/commands/index.cjs.map +10 -0
- package/dist/cjs/src/commands/ls/ls.cjs +119 -0
- package/dist/cjs/src/commands/ls/ls.cjs.map +10 -0
- package/dist/cjs/src/commands/mkdir/mkdir.cjs +64 -0
- package/dist/cjs/src/commands/mkdir/mkdir.cjs.map +10 -0
- package/dist/cjs/src/commands/mv/mv.cjs +118 -0
- package/dist/cjs/src/commands/mv/mv.cjs.map +10 -0
- package/dist/cjs/src/commands/pwd/pwd.cjs +41 -0
- package/dist/cjs/src/commands/pwd/pwd.cjs.map +10 -0
- package/dist/cjs/src/commands/rm/rm.cjs +82 -0
- package/dist/cjs/src/commands/rm/rm.cjs.map +10 -0
- package/dist/cjs/src/commands/sed/sed.cjs +218 -0
- package/dist/cjs/src/commands/sed/sed.cjs.map +10 -0
- package/dist/cjs/src/commands/sort/sort.cjs +105 -0
- package/dist/cjs/src/commands/sort/sort.cjs.map +10 -0
- package/dist/cjs/src/commands/tail/tail.cjs +95 -0
- package/dist/cjs/src/commands/tail/tail.cjs.map +10 -0
- package/dist/cjs/src/commands/tee/tee.cjs +65 -0
- package/dist/cjs/src/commands/tee/tee.cjs.map +10 -0
- package/dist/cjs/src/commands/test/test.cjs +148 -0
- package/dist/cjs/src/commands/test/test.cjs.map +10 -0
- package/dist/cjs/src/commands/touch/touch.cjs +70 -0
- package/dist/cjs/src/commands/touch/touch.cjs.map +10 -0
- package/dist/cjs/src/commands/tree/tree.cjs +161 -0
- package/dist/cjs/src/commands/tree/tree.cjs.map +10 -0
- package/dist/cjs/src/commands/true-false/true-false.cjs +43 -0
- package/dist/cjs/src/commands/true-false/true-false.cjs.map +10 -0
- package/dist/cjs/src/commands/uniq/uniq.cjs +116 -0
- package/dist/cjs/src/commands/uniq/uniq.cjs.map +10 -0
- package/dist/cjs/src/commands/wc/wc.cjs +116 -0
- package/dist/cjs/src/commands/wc/wc.cjs.map +10 -0
- package/dist/cjs/src/index.cjs +10 -2
- package/dist/cjs/src/index.cjs.map +3 -3
- package/dist/cjs/src/interpreter/index.cjs +4 -2
- package/dist/cjs/src/interpreter/index.cjs.map +3 -3
- package/dist/cjs/src/interpreter/interpreter.cjs +390 -2
- package/dist/cjs/src/interpreter/interpreter.cjs.map +3 -3
- package/dist/cjs/src/lexer/index.cjs +3 -2
- package/dist/cjs/src/lexer/index.cjs.map +3 -3
- package/dist/cjs/src/lexer/lexer.cjs +359 -8
- package/dist/cjs/src/lexer/lexer.cjs.map +3 -3
- package/dist/cjs/src/lexer/tokens.cjs +35 -2
- package/dist/cjs/src/lexer/tokens.cjs.map +3 -3
- package/dist/cjs/src/parser/ast.cjs +25 -1
- package/dist/cjs/src/parser/ast.cjs.map +3 -3
- package/dist/cjs/src/parser/index.cjs +7 -1
- package/dist/cjs/src/parser/index.cjs.map +3 -3
- package/dist/cjs/src/parser/parser.cjs +280 -8
- package/dist/cjs/src/parser/parser.cjs.map +3 -3
- package/dist/cjs/src/shell-dsl.cjs +2 -2
- package/dist/cjs/src/shell-dsl.cjs.map +3 -3
- package/dist/mjs/index.mjs +25 -5
- package/dist/mjs/index.mjs.map +3 -3
- package/dist/mjs/package.json +1 -1
- package/dist/mjs/src/commands/awk/awk.mjs +246 -0
- package/dist/mjs/src/commands/awk/awk.mjs.map +10 -0
- package/dist/mjs/src/commands/break-continue/break-continue.mjs +26 -0
- package/dist/mjs/src/commands/break-continue/break-continue.mjs.map +10 -0
- package/dist/mjs/src/commands/cat/cat.mjs +27 -0
- package/dist/mjs/src/commands/cat/cat.mjs.map +10 -0
- package/dist/mjs/src/commands/cp/cp.mjs +96 -0
- package/dist/mjs/src/commands/cp/cp.mjs.map +10 -0
- package/dist/mjs/src/commands/echo/echo.mjs +20 -0
- package/dist/mjs/src/commands/echo/echo.mjs.map +10 -0
- package/dist/mjs/src/commands/find/find.mjs +221 -0
- package/dist/mjs/src/commands/find/find.mjs.map +10 -0
- package/dist/mjs/src/commands/grep/grep.mjs +480 -0
- package/dist/mjs/src/commands/grep/grep.mjs.map +10 -0
- package/dist/mjs/src/commands/head/head.mjs +65 -0
- package/dist/mjs/src/commands/head/head.mjs.map +10 -0
- package/dist/mjs/src/commands/index.mjs +106 -0
- package/dist/mjs/src/commands/index.mjs.map +10 -0
- package/dist/mjs/src/commands/ls/ls.mjs +89 -0
- package/dist/mjs/src/commands/ls/ls.mjs.map +10 -0
- package/dist/mjs/src/commands/mkdir/mkdir.mjs +34 -0
- package/dist/mjs/src/commands/mkdir/mkdir.mjs.map +10 -0
- package/dist/mjs/src/commands/mv/mv.mjs +88 -0
- package/dist/mjs/src/commands/mv/mv.mjs.map +10 -0
- package/dist/mjs/src/commands/pwd/pwd.mjs +11 -0
- package/dist/mjs/src/commands/pwd/pwd.mjs.map +10 -0
- package/dist/mjs/src/commands/rm/rm.mjs +52 -0
- package/dist/mjs/src/commands/rm/rm.mjs.map +10 -0
- package/dist/mjs/src/commands/sed/sed.mjs +188 -0
- package/dist/mjs/src/commands/sed/sed.mjs.map +10 -0
- package/dist/mjs/src/commands/sort/sort.mjs +75 -0
- package/dist/mjs/src/commands/sort/sort.mjs.map +10 -0
- package/dist/mjs/src/commands/tail/tail.mjs +65 -0
- package/dist/mjs/src/commands/tail/tail.mjs.map +10 -0
- package/dist/mjs/src/commands/tee/tee.mjs +35 -0
- package/dist/mjs/src/commands/tee/tee.mjs.map +10 -0
- package/dist/mjs/src/commands/test/test.mjs +118 -0
- package/dist/mjs/src/commands/test/test.mjs.map +10 -0
- package/dist/mjs/src/commands/touch/touch.mjs +40 -0
- package/dist/mjs/src/commands/touch/touch.mjs.map +10 -0
- package/dist/mjs/src/commands/tree/tree.mjs +131 -0
- package/dist/mjs/src/commands/tree/tree.mjs.map +10 -0
- package/dist/mjs/src/commands/true-false/true-false.mjs +13 -0
- package/dist/mjs/src/commands/true-false/true-false.mjs.map +10 -0
- package/dist/mjs/src/commands/uniq/uniq.mjs +86 -0
- package/dist/mjs/src/commands/uniq/uniq.mjs.map +10 -0
- package/dist/mjs/src/commands/wc/wc.mjs +86 -0
- package/dist/mjs/src/commands/wc/wc.mjs.map +10 -0
- package/dist/mjs/src/index.mjs +18 -4
- package/dist/mjs/src/index.mjs.map +3 -3
- package/dist/mjs/src/interpreter/index.mjs +5 -3
- package/dist/mjs/src/interpreter/index.mjs.map +2 -2
- package/dist/mjs/src/interpreter/interpreter.mjs +390 -2
- package/dist/mjs/src/interpreter/interpreter.mjs.map +3 -3
- package/dist/mjs/src/lexer/index.mjs +4 -3
- package/dist/mjs/src/lexer/index.mjs.map +2 -2
- package/dist/mjs/src/lexer/lexer.mjs +359 -8
- package/dist/mjs/src/lexer/lexer.mjs.map +3 -3
- package/dist/mjs/src/lexer/tokens.mjs +35 -2
- package/dist/mjs/src/lexer/tokens.mjs.map +3 -3
- package/dist/mjs/src/parser/ast.mjs +25 -1
- package/dist/mjs/src/parser/ast.mjs.map +3 -3
- package/dist/mjs/src/parser/index.mjs +14 -2
- package/dist/mjs/src/parser/index.mjs.map +3 -3
- package/dist/mjs/src/parser/parser.mjs +280 -8
- package/dist/mjs/src/parser/parser.mjs.map +3 -3
- package/dist/mjs/src/shell-dsl.mjs +2 -2
- package/dist/mjs/src/shell-dsl.mjs.map +3 -3
- package/dist/types/index.d.ts +2 -2
- package/dist/types/src/commands/awk/awk.d.ts +2 -0
- package/dist/types/src/commands/break-continue/break-continue.d.ts +3 -0
- package/dist/types/src/commands/cat/cat.d.ts +2 -0
- package/dist/types/src/commands/cp/cp.d.ts +2 -0
- package/dist/types/src/commands/echo/echo.d.ts +2 -0
- package/dist/types/src/commands/find/find.d.ts +2 -0
- package/dist/types/src/commands/grep/grep.d.ts +2 -0
- package/dist/types/src/commands/head/head.d.ts +2 -0
- package/dist/types/src/commands/index.d.ts +25 -0
- package/dist/types/src/commands/ls/ls.d.ts +2 -0
- package/dist/types/src/commands/mkdir/mkdir.d.ts +2 -0
- package/dist/types/src/commands/mv/mv.d.ts +2 -0
- package/dist/types/src/commands/pwd/pwd.d.ts +2 -0
- package/dist/types/src/commands/rm/rm.d.ts +2 -0
- package/dist/types/src/commands/sed/sed.d.ts +2 -0
- package/dist/types/src/commands/sort/sort.d.ts +2 -0
- package/dist/types/src/commands/tail/tail.d.ts +2 -0
- package/dist/types/src/commands/tee/tee.d.ts +2 -0
- package/dist/types/{commands → src/commands/test}/test.d.ts +1 -1
- package/dist/types/src/commands/touch/touch.d.ts +2 -0
- package/dist/types/src/commands/tree/tree.d.ts +2 -0
- package/dist/types/{commands → src/commands/true-false}/true-false.d.ts +1 -1
- package/dist/types/src/commands/uniq/uniq.d.ts +2 -0
- package/dist/types/src/commands/wc/wc.d.ts +2 -0
- package/dist/types/src/index.d.ts +3 -3
- package/dist/types/src/interpreter/index.d.ts +1 -1
- package/dist/types/src/interpreter/interpreter.d.ts +18 -0
- package/dist/types/src/lexer/index.d.ts +2 -2
- package/dist/types/src/lexer/lexer.d.ts +16 -2
- package/dist/types/src/lexer/tokens.d.ts +20 -0
- package/dist/types/src/parser/ast.d.ts +47 -1
- package/dist/types/src/parser/index.d.ts +2 -2
- package/dist/types/src/parser/parser.d.ts +15 -0
- package/package.json +1 -1
- package/dist/types/commands/cat.d.ts +0 -2
- package/dist/types/commands/cp.d.ts +0 -2
- package/dist/types/commands/echo.d.ts +0 -2
- package/dist/types/commands/find.d.ts +0 -2
- package/dist/types/commands/grep.d.ts +0 -2
- package/dist/types/commands/head.d.ts +0 -2
- package/dist/types/commands/index.d.ts +0 -22
- package/dist/types/commands/ls.d.ts +0 -2
- package/dist/types/commands/mkdir.d.ts +0 -2
- package/dist/types/commands/mv.d.ts +0 -2
- package/dist/types/commands/pwd.d.ts +0 -2
- package/dist/types/commands/rm.d.ts +0 -2
- package/dist/types/commands/sort.d.ts +0 -2
- package/dist/types/commands/tail.d.ts +0 -2
- package/dist/types/commands/tee.d.ts +0 -2
- package/dist/types/commands/touch.d.ts +0 -2
- package/dist/types/commands/tree.d.ts +0 -2
- package/dist/types/commands/uniq.d.ts +0 -2
- package/dist/types/commands/wc.d.ts +0 -2
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/lexer/tokens.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export type RedirectMode =\n | \">\"\n | \">>\"\n | \"<\"\n | \"2>\"\n | \"2>>\"\n | \"&>\"\n | \"&>>\"\n | \"2>&1\"\n | \"1>&2\";\n\nexport type Token =\n | { type: \"word\"; value: string }\n | { type: \"pipe\" }\n | { type: \"and\" }\n | { type: \"or\" }\n | { type: \"semicolon\" }\n | { type: \"redirect\"; mode: RedirectMode }\n | { type: \"variable\"; name: string }\n | { type: \"substitution\"; command: string }\n | { type: \"glob\"; pattern: string }\n | { type: \"singleQuote\"; value: string }\n | { type: \"doubleQuote\"; parts: Array<string | Token> }\n | { type: \"assignment\"; name: string; value: string | Token[] }\n | { type: \"eof\" };\n\nexport function tokenToString(token: Token): string {\n switch (token.type) {\n case \"word\":\n return token.value;\n case \"pipe\":\n return \"|\";\n case \"and\":\n return \"&&\";\n case \"or\":\n return \"||\";\n case \"semicolon\":\n return \";\";\n case \"redirect\":\n return token.mode;\n case \"variable\":\n return `$${token.name}`;\n case \"substitution\":\n return `$(${token.command})`;\n case \"glob\":\n return token.pattern;\n case \"singleQuote\":\n return `'${token.value}'`;\n case \"doubleQuote\":\n return `\"${token.parts.map((p) => (typeof p === \"string\" ? p : tokenToString(p))).join(\"\")}\"`;\n case \"assignment\":\n return `${token.name}=${typeof token.value === \"string\" ? token.value : token.value.map(tokenToString).join(\"\")}`;\n case \"eof\":\n return \"<EOF>\";\n }\n}\n"
|
|
5
|
+
"export type RedirectMode =\n | \">\"\n | \">>\"\n | \"<\"\n | \"2>\"\n | \"2>>\"\n | \"&>\"\n | \"&>>\"\n | \"2>&1\"\n | \"1>&2\";\n\nexport type KeywordValue =\n | \"if\"\n | \"then\"\n | \"elif\"\n | \"else\"\n | \"fi\"\n | \"for\"\n | \"in\"\n | \"do\"\n | \"done\"\n | \"while\"\n | \"until\"\n | \"case\"\n | \"esac\";\n\nexport const KEYWORDS = new Set<string>([\n \"if\",\n \"then\",\n \"elif\",\n \"else\",\n \"fi\",\n \"for\",\n \"in\",\n \"do\",\n \"done\",\n \"while\",\n \"until\",\n \"case\",\n \"esac\",\n]);\n\nexport type Token =\n | { type: \"word\"; value: string }\n | { type: \"keyword\"; value: KeywordValue }\n | { type: \"pipe\" }\n | { type: \"and\" }\n | { type: \"or\" }\n | { type: \"semicolon\" }\n | { type: \"newline\" }\n | { type: \"redirect\"; mode: RedirectMode }\n | { type: \"variable\"; name: string }\n | { type: \"substitution\"; command: string }\n | { type: \"arithmetic\"; expression: string }\n | { type: \"glob\"; pattern: string }\n | { type: \"singleQuote\"; value: string }\n | { type: \"doubleQuote\"; parts: Array<string | Token> }\n | { type: \"assignment\"; name: string; value: string | Token[] }\n | { type: \"heredoc\"; content: string; expand: boolean }\n | { type: \"openParen\" }\n | { type: \"closeParen\" }\n | { type: \"doubleSemicolon\" }\n | { type: \"eof\" };\n\nexport function tokenToString(token: Token): string {\n switch (token.type) {\n case \"word\":\n return token.value;\n case \"keyword\":\n return token.value;\n case \"pipe\":\n return \"|\";\n case \"and\":\n return \"&&\";\n case \"or\":\n return \"||\";\n case \"semicolon\":\n return \";\";\n case \"newline\":\n return \"\\n\";\n case \"redirect\":\n return token.mode;\n case \"variable\":\n return `$${token.name}`;\n case \"substitution\":\n return `$(${token.command})`;\n case \"arithmetic\":\n return `$((${token.expression}))`;\n case \"glob\":\n return token.pattern;\n case \"singleQuote\":\n return `'${token.value}'`;\n case \"doubleQuote\":\n return `\"${token.parts.map((p) => (typeof p === \"string\" ? p : tokenToString(p))).join(\"\")}\"`;\n case \"assignment\":\n return `${token.name}=${typeof token.value === \"string\" ? token.value : token.value.map(tokenToString).join(\"\")}`;\n case \"heredoc\":\n return `<<${token.expand ? \"EOF\" : \"'EOF'\"}\\n${token.content}\\nEOF`;\n case \"openParen\":\n return \"(\";\n case \"closeParen\":\n return \")\";\n case \"doubleSemicolon\":\n return \";;\";\n case \"eof\":\n return \"<EOF>\";\n }\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AA0BO,SAAS,aAAa,CAAC,OAAsB;AAAA,EAClD,QAAQ,MAAM;AAAA,SACP;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO,IAAI,MAAM;AAAA,SACd;AAAA,MACH,OAAO,KAAK,MAAM;AAAA,SACf;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO,IAAI,MAAM;AAAA,SACd;AAAA,MACH,OAAO,IAAI,MAAM,MAAM,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,cAAc,CAAC,CAAE,EAAE,KAAK,EAAE;AAAA,SACtF;AAAA,MACH,OAAO,GAAG,MAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,MAAM,MAAM,IAAI,aAAa,EAAE,KAAK,EAAE;AAAA,SAC3G;AAAA,MACH,OAAO;AAAA;AAAA;",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AA0BO,IAAM,WAAW,IAAI,IAAY;AAAA,EACtC;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAwBM,SAAS,aAAa,CAAC,OAAsB;AAAA,EAClD,QAAQ,MAAM;AAAA,SACP;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA;AAAA,SACJ;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO,IAAI,MAAM;AAAA,SACd;AAAA,MACH,OAAO,KAAK,MAAM;AAAA,SACf;AAAA,MACH,OAAO,MAAM,MAAM;AAAA,SAChB;AAAA,MACH,OAAO,MAAM;AAAA,SACV;AAAA,MACH,OAAO,IAAI,MAAM;AAAA,SACd;AAAA,MACH,OAAO,IAAI,MAAM,MAAM,IAAI,CAAC,MAAO,OAAO,MAAM,WAAW,IAAI,cAAc,CAAC,CAAE,EAAE,KAAK,EAAE;AAAA,SACtF;AAAA,MACH,OAAO,GAAG,MAAM,QAAQ,OAAO,MAAM,UAAU,WAAW,MAAM,QAAQ,MAAM,MAAM,IAAI,aAAa,EAAE,KAAK,EAAE;AAAA,SAC3G;AAAA,MACH,OAAO,KAAK,MAAM,SAAS,QAAQ;AAAA,EAAY,MAAM;AAAA;AAAA,SAClD;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA,SACJ;AAAA,MACH,OAAO;AAAA;AAAA;",
|
|
8
|
+
"debugId": "6DFA4E9D27E55DE464756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -29,17 +29,41 @@ function isGlobNode(node) {
|
|
|
29
29
|
function isConcatNode(node) {
|
|
30
30
|
return node.type === "concat";
|
|
31
31
|
}
|
|
32
|
+
function isIfNode(node) {
|
|
33
|
+
return node.type === "if";
|
|
34
|
+
}
|
|
35
|
+
function isForNode(node) {
|
|
36
|
+
return node.type === "for";
|
|
37
|
+
}
|
|
38
|
+
function isWhileNode(node) {
|
|
39
|
+
return node.type === "while";
|
|
40
|
+
}
|
|
41
|
+
function isUntilNode(node) {
|
|
42
|
+
return node.type === "until";
|
|
43
|
+
}
|
|
44
|
+
function isCaseNode(node) {
|
|
45
|
+
return node.type === "case";
|
|
46
|
+
}
|
|
47
|
+
function isArithmeticNode(node) {
|
|
48
|
+
return node.type === "arithmetic";
|
|
49
|
+
}
|
|
32
50
|
export {
|
|
51
|
+
isWhileNode,
|
|
33
52
|
isVariableNode,
|
|
53
|
+
isUntilNode,
|
|
34
54
|
isSubstitutionNode,
|
|
35
55
|
isSequenceNode,
|
|
36
56
|
isPipelineNode,
|
|
37
57
|
isOrNode,
|
|
38
58
|
isLiteralNode,
|
|
59
|
+
isIfNode,
|
|
39
60
|
isGlobNode,
|
|
61
|
+
isForNode,
|
|
40
62
|
isConcatNode,
|
|
41
63
|
isCommandNode,
|
|
64
|
+
isCaseNode,
|
|
65
|
+
isArithmeticNode,
|
|
42
66
|
isAndNode
|
|
43
67
|
};
|
|
44
68
|
|
|
45
|
-
//# debugId=
|
|
69
|
+
//# debugId=02AD1F7B6F799FCB64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/parser/ast.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export type RedirectMode =\n | \">\"\n | \">>\"\n | \"<\"\n | \"2>\"\n | \"2>>\"\n | \"&>\"\n | \"&>>\"\n | \"2>&1\"\n | \"1>&2\";\n\nexport interface Redirect {\n mode: RedirectMode;\n target: ASTNode;\n}\n\nexport type ASTNode =\n | CommandNode\n | PipelineNode\n | AndNode\n | OrNode\n | SequenceNode\n | LiteralNode\n | VariableNode\n | SubstitutionNode\n | GlobNode\n | ConcatNode;\n\nexport interface CommandNode {\n type: \"command\";\n name: ASTNode;\n args: ASTNode[];\n redirects: Redirect[];\n assignments: Array<{ name: string; value: ASTNode }>;\n}\n\nexport interface PipelineNode {\n type: \"pipeline\";\n commands: ASTNode[];\n}\n\nexport interface AndNode {\n type: \"and\";\n left: ASTNode;\n right: ASTNode;\n}\n\nexport interface OrNode {\n type: \"or\";\n left: ASTNode;\n right: ASTNode;\n}\n\nexport interface SequenceNode {\n type: \"sequence\";\n commands: ASTNode[];\n}\n\nexport interface LiteralNode {\n type: \"literal\";\n value: string;\n}\n\nexport interface VariableNode {\n type: \"variable\";\n name: string;\n}\n\nexport interface SubstitutionNode {\n type: \"substitution\";\n command: ASTNode;\n}\n\nexport interface GlobNode {\n type: \"glob\";\n pattern: string;\n}\n\nexport interface ConcatNode {\n type: \"concat\";\n parts: ASTNode[];\n}\n\n// Type guards\nexport function isCommandNode(node: ASTNode): node is CommandNode {\n return node.type === \"command\";\n}\n\nexport function isPipelineNode(node: ASTNode): node is PipelineNode {\n return node.type === \"pipeline\";\n}\n\nexport function isAndNode(node: ASTNode): node is AndNode {\n return node.type === \"and\";\n}\n\nexport function isOrNode(node: ASTNode): node is OrNode {\n return node.type === \"or\";\n}\n\nexport function isSequenceNode(node: ASTNode): node is SequenceNode {\n return node.type === \"sequence\";\n}\n\nexport function isLiteralNode(node: ASTNode): node is LiteralNode {\n return node.type === \"literal\";\n}\n\nexport function isVariableNode(node: ASTNode): node is VariableNode {\n return node.type === \"variable\";\n}\n\nexport function isSubstitutionNode(node: ASTNode): node is SubstitutionNode {\n return node.type === \"substitution\";\n}\n\nexport function isGlobNode(node: ASTNode): node is GlobNode {\n return node.type === \"glob\";\n}\n\nexport function isConcatNode(node: ASTNode): node is ConcatNode {\n return node.type === \"concat\";\n}\n"
|
|
5
|
+
"export type RedirectMode =\n | \">\"\n | \">>\"\n | \"<\"\n | \"2>\"\n | \"2>>\"\n | \"&>\"\n | \"&>>\"\n | \"2>&1\"\n | \"1>&2\";\n\nexport interface Redirect {\n mode: RedirectMode;\n target: ASTNode;\n heredocContent?: boolean;\n}\n\nexport type ASTNode =\n | CommandNode\n | PipelineNode\n | AndNode\n | OrNode\n | SequenceNode\n | LiteralNode\n | VariableNode\n | SubstitutionNode\n | GlobNode\n | ConcatNode\n | IfNode\n | ForNode\n | WhileNode\n | UntilNode\n | CaseNode\n | ArithmeticNode;\n\nexport interface CommandNode {\n type: \"command\";\n name: ASTNode;\n args: ASTNode[];\n redirects: Redirect[];\n assignments: Array<{ name: string; value: ASTNode }>;\n}\n\nexport interface PipelineNode {\n type: \"pipeline\";\n commands: ASTNode[];\n}\n\nexport interface AndNode {\n type: \"and\";\n left: ASTNode;\n right: ASTNode;\n}\n\nexport interface OrNode {\n type: \"or\";\n left: ASTNode;\n right: ASTNode;\n}\n\nexport interface SequenceNode {\n type: \"sequence\";\n commands: ASTNode[];\n}\n\nexport interface LiteralNode {\n type: \"literal\";\n value: string;\n}\n\nexport interface VariableNode {\n type: \"variable\";\n name: string;\n}\n\nexport interface SubstitutionNode {\n type: \"substitution\";\n command: ASTNode;\n}\n\nexport interface GlobNode {\n type: \"glob\";\n pattern: string;\n}\n\nexport interface ConcatNode {\n type: \"concat\";\n parts: ASTNode[];\n}\n\nexport interface IfNode {\n type: \"if\";\n condition: ASTNode;\n thenBranch: ASTNode;\n elifBranches: Array<{ condition: ASTNode; body: ASTNode }>;\n elseBranch?: ASTNode;\n}\n\nexport interface ForNode {\n type: \"for\";\n variable: string;\n items: ASTNode[];\n body: ASTNode;\n}\n\nexport interface WhileNode {\n type: \"while\";\n condition: ASTNode;\n body: ASTNode;\n}\n\nexport interface UntilNode {\n type: \"until\";\n condition: ASTNode;\n body: ASTNode;\n}\n\nexport interface CaseClause {\n patterns: ASTNode[];\n body: ASTNode;\n}\n\nexport interface CaseNode {\n type: \"case\";\n word: ASTNode;\n clauses: CaseClause[];\n}\n\nexport interface ArithmeticNode {\n type: \"arithmetic\";\n expression: string;\n}\n\n// Type guards\nexport function isCommandNode(node: ASTNode): node is CommandNode {\n return node.type === \"command\";\n}\n\nexport function isPipelineNode(node: ASTNode): node is PipelineNode {\n return node.type === \"pipeline\";\n}\n\nexport function isAndNode(node: ASTNode): node is AndNode {\n return node.type === \"and\";\n}\n\nexport function isOrNode(node: ASTNode): node is OrNode {\n return node.type === \"or\";\n}\n\nexport function isSequenceNode(node: ASTNode): node is SequenceNode {\n return node.type === \"sequence\";\n}\n\nexport function isLiteralNode(node: ASTNode): node is LiteralNode {\n return node.type === \"literal\";\n}\n\nexport function isVariableNode(node: ASTNode): node is VariableNode {\n return node.type === \"variable\";\n}\n\nexport function isSubstitutionNode(node: ASTNode): node is SubstitutionNode {\n return node.type === \"substitution\";\n}\n\nexport function isGlobNode(node: ASTNode): node is GlobNode {\n return node.type === \"glob\";\n}\n\nexport function isConcatNode(node: ASTNode): node is ConcatNode {\n return node.type === \"concat\";\n}\n\nexport function isIfNode(node: ASTNode): node is IfNode {\n return node.type === \"if\";\n}\n\nexport function isForNode(node: ASTNode): node is ForNode {\n return node.type === \"for\";\n}\n\nexport function isWhileNode(node: ASTNode): node is WhileNode {\n return node.type === \"while\";\n}\n\nexport function isUntilNode(node: ASTNode): node is UntilNode {\n return node.type === \"until\";\n}\n\nexport function isCaseNode(node: ASTNode): node is CaseNode {\n return node.type === \"case\";\n}\n\nexport function isArithmeticNode(node: ASTNode): node is ArithmeticNode {\n return node.type === \"arithmetic\";\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAsIO,SAAS,aAAa,CAAC,MAAoC;AAAA,EAChE,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,cAAc,CAAC,MAAqC;AAAA,EAClE,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,SAAS,CAAC,MAAgC;AAAA,EACxD,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,QAAQ,CAAC,MAA+B;AAAA,EACtD,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,cAAc,CAAC,MAAqC;AAAA,EAClE,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,aAAa,CAAC,MAAoC;AAAA,EAChE,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,cAAc,CAAC,MAAqC;AAAA,EAClE,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,kBAAkB,CAAC,MAAyC;AAAA,EAC1E,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,UAAU,CAAC,MAAiC;AAAA,EAC1D,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,YAAY,CAAC,MAAmC;AAAA,EAC9D,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,QAAQ,CAAC,MAA+B;AAAA,EACtD,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,SAAS,CAAC,MAAgC;AAAA,EACxD,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,WAAW,CAAC,MAAkC;AAAA,EAC5D,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,WAAW,CAAC,MAAkC;AAAA,EAC5D,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,UAAU,CAAC,MAAiC;AAAA,EAC1D,OAAO,KAAK,SAAS;AAAA;AAGhB,SAAS,gBAAgB,CAAC,MAAuC;AAAA,EACtE,OAAO,KAAK,SAAS;AAAA;",
|
|
8
|
+
"debugId": "02AD1F7B6F799FCB64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -10,21 +10,33 @@ import {
|
|
|
10
10
|
isVariableNode,
|
|
11
11
|
isSubstitutionNode,
|
|
12
12
|
isGlobNode,
|
|
13
|
-
isConcatNode
|
|
13
|
+
isConcatNode,
|
|
14
|
+
isIfNode,
|
|
15
|
+
isForNode,
|
|
16
|
+
isWhileNode,
|
|
17
|
+
isUntilNode,
|
|
18
|
+
isCaseNode,
|
|
19
|
+
isArithmeticNode
|
|
14
20
|
} from "./ast.mjs";
|
|
15
21
|
export {
|
|
16
22
|
parse,
|
|
23
|
+
isWhileNode,
|
|
17
24
|
isVariableNode,
|
|
25
|
+
isUntilNode,
|
|
18
26
|
isSubstitutionNode,
|
|
19
27
|
isSequenceNode,
|
|
20
28
|
isPipelineNode,
|
|
21
29
|
isOrNode,
|
|
22
30
|
isLiteralNode,
|
|
31
|
+
isIfNode,
|
|
23
32
|
isGlobNode,
|
|
33
|
+
isForNode,
|
|
24
34
|
isConcatNode,
|
|
25
35
|
isCommandNode,
|
|
36
|
+
isCaseNode,
|
|
37
|
+
isArithmeticNode,
|
|
26
38
|
isAndNode,
|
|
27
39
|
Parser
|
|
28
40
|
};
|
|
29
41
|
|
|
30
|
-
//# debugId=
|
|
42
|
+
//# debugId=994277F223D738D664756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/parser/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"export { Parser, parse } from \"./parser.mjs\";\nexport type {\n ASTNode,\n Redirect,\n RedirectMode,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n LiteralNode,\n VariableNode,\n SubstitutionNode,\n GlobNode,\n ConcatNode,\n} from \"./ast.mjs\";\nexport {\n isCommandNode,\n isPipelineNode,\n isAndNode,\n isOrNode,\n isSequenceNode,\n isLiteralNode,\n isVariableNode,\n isSubstitutionNode,\n isGlobNode,\n isConcatNode,\n} from \"./ast.mjs\";\n"
|
|
5
|
+
"export { Parser, parse } from \"./parser.mjs\";\nexport type {\n ASTNode,\n Redirect,\n RedirectMode,\n CommandNode,\n PipelineNode,\n AndNode,\n OrNode,\n SequenceNode,\n LiteralNode,\n VariableNode,\n SubstitutionNode,\n GlobNode,\n ConcatNode,\n IfNode,\n ForNode,\n WhileNode,\n UntilNode,\n CaseNode,\n CaseClause,\n ArithmeticNode,\n} from \"./ast.mjs\";\nexport {\n isCommandNode,\n isPipelineNode,\n isAndNode,\n isOrNode,\n isSequenceNode,\n isLiteralNode,\n isVariableNode,\n isSubstitutionNode,\n isGlobNode,\n isConcatNode,\n isIfNode,\n isForNode,\n isWhileNode,\n isUntilNode,\n isCaseNode,\n isArithmeticNode,\n} from \"./ast.mjs\";\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";AAAA;
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";AAAA;AAuBA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
|
|
8
|
+
"debugId": "994277F223D738D664756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -18,10 +18,12 @@ class Parser {
|
|
|
18
18
|
return result;
|
|
19
19
|
}
|
|
20
20
|
parseSequence() {
|
|
21
|
+
this.skipNewlines();
|
|
21
22
|
const commands = [];
|
|
22
23
|
commands.push(this.parseAndOr());
|
|
23
|
-
while (this.match("semicolon")) {
|
|
24
|
-
|
|
24
|
+
while (this.match("semicolon") || this.match("newline")) {
|
|
25
|
+
this.skipNewlines();
|
|
26
|
+
if (this.isAtEnd() || this.check("semicolon") || this.check("newline") || this.isTerminatingKeyword())
|
|
25
27
|
continue;
|
|
26
28
|
commands.push(this.parseAndOr());
|
|
27
29
|
}
|
|
@@ -30,6 +32,15 @@ class Parser {
|
|
|
30
32
|
}
|
|
31
33
|
return { type: "sequence", commands };
|
|
32
34
|
}
|
|
35
|
+
skipNewlines() {
|
|
36
|
+
while (this.match("newline")) {}
|
|
37
|
+
}
|
|
38
|
+
isTerminatingKeyword() {
|
|
39
|
+
const token = this.peek();
|
|
40
|
+
if (token.type !== "keyword")
|
|
41
|
+
return false;
|
|
42
|
+
return ["then", "elif", "else", "fi", "do", "done", "esac"].includes(token.value);
|
|
43
|
+
}
|
|
33
44
|
parseAndOr() {
|
|
34
45
|
let left = this.parsePipeline();
|
|
35
46
|
while (this.check("and") || this.check("or")) {
|
|
@@ -45,15 +56,206 @@ class Parser {
|
|
|
45
56
|
}
|
|
46
57
|
parsePipeline() {
|
|
47
58
|
const commands = [];
|
|
48
|
-
commands.push(this.
|
|
59
|
+
commands.push(this.parseCompoundOrCommand());
|
|
49
60
|
while (this.match("pipe")) {
|
|
50
|
-
|
|
61
|
+
this.skipNewlines();
|
|
62
|
+
commands.push(this.parseCompoundOrCommand());
|
|
51
63
|
}
|
|
52
64
|
if (commands.length === 1) {
|
|
53
65
|
return commands[0];
|
|
54
66
|
}
|
|
55
67
|
return { type: "pipeline", commands };
|
|
56
68
|
}
|
|
69
|
+
parseCompoundOrCommand() {
|
|
70
|
+
this.skipNewlines();
|
|
71
|
+
const token = this.peek();
|
|
72
|
+
if (token.type === "keyword") {
|
|
73
|
+
switch (token.value) {
|
|
74
|
+
case "if":
|
|
75
|
+
return this.parseIf();
|
|
76
|
+
case "for":
|
|
77
|
+
return this.parseFor();
|
|
78
|
+
case "while":
|
|
79
|
+
return this.parseWhile();
|
|
80
|
+
case "until":
|
|
81
|
+
return this.parseUntil();
|
|
82
|
+
case "case":
|
|
83
|
+
return this.parseCase();
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return this.parseCommand();
|
|
87
|
+
}
|
|
88
|
+
parseIf() {
|
|
89
|
+
this.expectKeyword("if");
|
|
90
|
+
const condition = this.parseCompoundList(["then"]);
|
|
91
|
+
this.expectKeyword("then");
|
|
92
|
+
const thenBranch = this.parseCompoundList(["elif", "else", "fi"]);
|
|
93
|
+
const elifBranches = [];
|
|
94
|
+
while (this.checkKeyword("elif")) {
|
|
95
|
+
this.expectKeyword("elif");
|
|
96
|
+
const elifCondition = this.parseCompoundList(["then"]);
|
|
97
|
+
this.expectKeyword("then");
|
|
98
|
+
const elifBody = this.parseCompoundList(["elif", "else", "fi"]);
|
|
99
|
+
elifBranches.push({ condition: elifCondition, body: elifBody });
|
|
100
|
+
}
|
|
101
|
+
let elseBranch;
|
|
102
|
+
if (this.checkKeyword("else")) {
|
|
103
|
+
this.expectKeyword("else");
|
|
104
|
+
elseBranch = this.parseCompoundList(["fi"]);
|
|
105
|
+
}
|
|
106
|
+
this.expectKeyword("fi");
|
|
107
|
+
return {
|
|
108
|
+
type: "if",
|
|
109
|
+
condition,
|
|
110
|
+
thenBranch,
|
|
111
|
+
elifBranches,
|
|
112
|
+
elseBranch
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
parseFor() {
|
|
116
|
+
this.expectKeyword("for");
|
|
117
|
+
const varToken = this.peek();
|
|
118
|
+
if (varToken.type !== "word") {
|
|
119
|
+
throw new ParseError("Expected variable name after 'for'");
|
|
120
|
+
}
|
|
121
|
+
this.advance();
|
|
122
|
+
const variable = varToken.value;
|
|
123
|
+
const items = [];
|
|
124
|
+
if (this.checkKeyword("in")) {
|
|
125
|
+
this.expectKeyword("in");
|
|
126
|
+
while (!this.isAtEnd() && !this.check("semicolon") && !this.check("newline") && !this.checkKeyword("do")) {
|
|
127
|
+
items.push(this.parseWordArg());
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
if (!this.match("semicolon")) {
|
|
131
|
+
this.match("newline");
|
|
132
|
+
}
|
|
133
|
+
this.skipNewlines();
|
|
134
|
+
this.expectKeyword("do");
|
|
135
|
+
const body = this.parseCompoundList(["done"]);
|
|
136
|
+
this.expectKeyword("done");
|
|
137
|
+
return {
|
|
138
|
+
type: "for",
|
|
139
|
+
variable,
|
|
140
|
+
items,
|
|
141
|
+
body
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
parseWhile() {
|
|
145
|
+
this.expectKeyword("while");
|
|
146
|
+
const condition = this.parseCompoundList(["do"]);
|
|
147
|
+
this.expectKeyword("do");
|
|
148
|
+
const body = this.parseCompoundList(["done"]);
|
|
149
|
+
this.expectKeyword("done");
|
|
150
|
+
return {
|
|
151
|
+
type: "while",
|
|
152
|
+
condition,
|
|
153
|
+
body
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
parseUntil() {
|
|
157
|
+
this.expectKeyword("until");
|
|
158
|
+
const condition = this.parseCompoundList(["do"]);
|
|
159
|
+
this.expectKeyword("do");
|
|
160
|
+
const body = this.parseCompoundList(["done"]);
|
|
161
|
+
this.expectKeyword("done");
|
|
162
|
+
return {
|
|
163
|
+
type: "until",
|
|
164
|
+
condition,
|
|
165
|
+
body
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
parseCase() {
|
|
169
|
+
this.expectKeyword("case");
|
|
170
|
+
const word = this.parseWordArg();
|
|
171
|
+
this.expectKeyword("in");
|
|
172
|
+
this.skipNewlines();
|
|
173
|
+
const clauses = [];
|
|
174
|
+
while (!this.isAtEnd() && !this.checkKeyword("esac")) {
|
|
175
|
+
this.match("openParen");
|
|
176
|
+
const patterns = [];
|
|
177
|
+
patterns.push(this.parseCasePattern());
|
|
178
|
+
while (this.match("pipe")) {
|
|
179
|
+
patterns.push(this.parseCasePattern());
|
|
180
|
+
}
|
|
181
|
+
if (!this.match("closeParen")) {
|
|
182
|
+
throw new ParseError("Expected ')' after case pattern");
|
|
183
|
+
}
|
|
184
|
+
const body = this.parseCaseBody();
|
|
185
|
+
clauses.push({ patterns, body });
|
|
186
|
+
this.match("doubleSemicolon");
|
|
187
|
+
this.skipNewlines();
|
|
188
|
+
}
|
|
189
|
+
this.expectKeyword("esac");
|
|
190
|
+
return {
|
|
191
|
+
type: "case",
|
|
192
|
+
word,
|
|
193
|
+
clauses
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
parseCasePattern() {
|
|
197
|
+
const token = this.peek();
|
|
198
|
+
if (token.type === "word" || token.type === "glob" || token.type === "singleQuote" || token.type === "doubleQuote") {
|
|
199
|
+
return this.parseWordArg();
|
|
200
|
+
}
|
|
201
|
+
throw new ParseError(`Expected pattern in case clause, got ${token.type}`);
|
|
202
|
+
}
|
|
203
|
+
parseCaseBody() {
|
|
204
|
+
const commands = [];
|
|
205
|
+
this.skipNewlines();
|
|
206
|
+
while (!this.isAtEnd() && !this.check("doubleSemicolon") && !this.checkKeyword("esac")) {
|
|
207
|
+
commands.push(this.parseAndOr());
|
|
208
|
+
if (!this.match("semicolon") && !this.match("newline")) {
|
|
209
|
+
break;
|
|
210
|
+
}
|
|
211
|
+
this.skipNewlines();
|
|
212
|
+
if (this.check("doubleSemicolon") || this.checkKeyword("esac")) {
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (commands.length === 0) {
|
|
217
|
+
return { type: "command", name: { type: "literal", value: "true" }, args: [], redirects: [], assignments: [] };
|
|
218
|
+
}
|
|
219
|
+
if (commands.length === 1) {
|
|
220
|
+
return commands[0];
|
|
221
|
+
}
|
|
222
|
+
return { type: "sequence", commands };
|
|
223
|
+
}
|
|
224
|
+
parseCompoundList(terminators) {
|
|
225
|
+
this.skipNewlines();
|
|
226
|
+
const commands = [];
|
|
227
|
+
if (this.isCompoundListTerminator(terminators)) {
|
|
228
|
+
return { type: "command", name: { type: "literal", value: "true" }, args: [], redirects: [], assignments: [] };
|
|
229
|
+
}
|
|
230
|
+
commands.push(this.parseAndOr());
|
|
231
|
+
while ((this.match("semicolon") || this.match("newline")) && !this.isAtEnd()) {
|
|
232
|
+
this.skipNewlines();
|
|
233
|
+
if (this.isCompoundListTerminator(terminators)) {
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
commands.push(this.parseAndOr());
|
|
237
|
+
}
|
|
238
|
+
if (commands.length === 1) {
|
|
239
|
+
return commands[0];
|
|
240
|
+
}
|
|
241
|
+
return { type: "sequence", commands };
|
|
242
|
+
}
|
|
243
|
+
isCompoundListTerminator(terminators) {
|
|
244
|
+
const token = this.peek();
|
|
245
|
+
if (token.type !== "keyword")
|
|
246
|
+
return false;
|
|
247
|
+
return terminators.includes(token.value);
|
|
248
|
+
}
|
|
249
|
+
checkKeyword(value) {
|
|
250
|
+
const token = this.peek();
|
|
251
|
+
return token.type === "keyword" && token.value === value;
|
|
252
|
+
}
|
|
253
|
+
expectKeyword(value) {
|
|
254
|
+
if (!this.checkKeyword(value)) {
|
|
255
|
+
throw new ParseError(`Expected '${value}'`);
|
|
256
|
+
}
|
|
257
|
+
this.advance();
|
|
258
|
+
}
|
|
57
259
|
parseCommand() {
|
|
58
260
|
const assignments = [];
|
|
59
261
|
const args = [];
|
|
@@ -66,7 +268,16 @@ class Parser {
|
|
|
66
268
|
});
|
|
67
269
|
}
|
|
68
270
|
while (this.isWordToken()) {
|
|
69
|
-
|
|
271
|
+
if (this.peek().type === "heredoc") {
|
|
272
|
+
const heredocToken = this.advance();
|
|
273
|
+
redirects.push({
|
|
274
|
+
mode: "<",
|
|
275
|
+
target: this.tokenToNode(heredocToken),
|
|
276
|
+
heredocContent: true
|
|
277
|
+
});
|
|
278
|
+
} else {
|
|
279
|
+
args.push(this.parseWordArg());
|
|
280
|
+
}
|
|
70
281
|
}
|
|
71
282
|
while (this.check("redirect")) {
|
|
72
283
|
const redirect = this.parseRedirect();
|
|
@@ -111,12 +322,19 @@ class Parser {
|
|
|
111
322
|
case "variable":
|
|
112
323
|
return { type: "variable", name: token.name };
|
|
113
324
|
case "substitution":
|
|
114
|
-
const innerParser = new Parser(new (__require("../lexer/lexer.ts")).Lexer(token.command).tokenize());
|
|
325
|
+
const innerParser = new Parser(new (__require("../lexer/lexer.ts")).Lexer(token.command, { preserveNewlines: true }).tokenize());
|
|
115
326
|
return { type: "substitution", command: innerParser.parse() };
|
|
327
|
+
case "arithmetic":
|
|
328
|
+
return { type: "arithmetic", expression: token.expression };
|
|
116
329
|
case "glob":
|
|
117
330
|
return { type: "glob", pattern: token.pattern };
|
|
118
331
|
case "assignment":
|
|
119
332
|
return this.tokenToNode(token.value);
|
|
333
|
+
case "heredoc":
|
|
334
|
+
if (token.expand) {
|
|
335
|
+
return this.parseHeredocContent(token.content);
|
|
336
|
+
}
|
|
337
|
+
return { type: "literal", value: token.content };
|
|
120
338
|
default:
|
|
121
339
|
throw new ParseError(`Unexpected token type: ${token.type}`);
|
|
122
340
|
}
|
|
@@ -140,6 +358,60 @@ class Parser {
|
|
|
140
358
|
});
|
|
141
359
|
return { type: "concat", parts: nodes };
|
|
142
360
|
}
|
|
361
|
+
parseHeredocContent(content) {
|
|
362
|
+
const parts = [];
|
|
363
|
+
let currentLiteral = "";
|
|
364
|
+
let i = 0;
|
|
365
|
+
while (i < content.length) {
|
|
366
|
+
if (content[i] === "$") {
|
|
367
|
+
if (currentLiteral) {
|
|
368
|
+
parts.push({ type: "literal", value: currentLiteral });
|
|
369
|
+
currentLiteral = "";
|
|
370
|
+
}
|
|
371
|
+
i++;
|
|
372
|
+
if (i >= content.length) {
|
|
373
|
+
currentLiteral += "$";
|
|
374
|
+
break;
|
|
375
|
+
}
|
|
376
|
+
if (content[i] === "{") {
|
|
377
|
+
i++;
|
|
378
|
+
let varName = "";
|
|
379
|
+
while (i < content.length && content[i] !== "}") {
|
|
380
|
+
varName += content[i];
|
|
381
|
+
i++;
|
|
382
|
+
}
|
|
383
|
+
if (i < content.length && content[i] === "}") {
|
|
384
|
+
i++;
|
|
385
|
+
}
|
|
386
|
+
if (varName) {
|
|
387
|
+
parts.push({ type: "variable", name: varName });
|
|
388
|
+
}
|
|
389
|
+
} else if (/[a-zA-Z_]/.test(content[i] ?? "")) {
|
|
390
|
+
let varName = "";
|
|
391
|
+
while (i < content.length && /[a-zA-Z0-9_]/.test(content[i] ?? "")) {
|
|
392
|
+
varName += content[i];
|
|
393
|
+
i++;
|
|
394
|
+
}
|
|
395
|
+
parts.push({ type: "variable", name: varName });
|
|
396
|
+
} else {
|
|
397
|
+
currentLiteral += "$";
|
|
398
|
+
}
|
|
399
|
+
} else {
|
|
400
|
+
currentLiteral += content[i];
|
|
401
|
+
i++;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
if (currentLiteral) {
|
|
405
|
+
parts.push({ type: "literal", value: currentLiteral });
|
|
406
|
+
}
|
|
407
|
+
if (parts.length === 0) {
|
|
408
|
+
return { type: "literal", value: "" };
|
|
409
|
+
}
|
|
410
|
+
if (parts.length === 1) {
|
|
411
|
+
return parts[0];
|
|
412
|
+
}
|
|
413
|
+
return { type: "concat", parts };
|
|
414
|
+
}
|
|
143
415
|
parseRedirect() {
|
|
144
416
|
const token = this.advance();
|
|
145
417
|
const mode = token.mode;
|
|
@@ -154,7 +426,7 @@ class Parser {
|
|
|
154
426
|
}
|
|
155
427
|
isWordToken() {
|
|
156
428
|
const token = this.peek();
|
|
157
|
-
return token.type === "word" || token.type === "singleQuote" || token.type === "doubleQuote" || token.type === "variable" || token.type === "substitution" || token.type === "glob";
|
|
429
|
+
return token.type === "word" || token.type === "singleQuote" || token.type === "doubleQuote" || token.type === "variable" || token.type === "substitution" || token.type === "arithmetic" || token.type === "glob" || token.type === "heredoc";
|
|
158
430
|
}
|
|
159
431
|
check(type) {
|
|
160
432
|
return this.peek().type === type;
|
|
@@ -186,4 +458,4 @@ export {
|
|
|
186
458
|
Parser
|
|
187
459
|
};
|
|
188
460
|
|
|
189
|
-
//# debugId=
|
|
461
|
+
//# debugId=D4EE93E6428C592D64756E2164756E21
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/parser/parser.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { ParseError } from \"../errors.mjs\";\nimport type { Token } from \"../lexer/tokens.mjs\";\nimport type { ASTNode, Redirect, RedirectMode, CommandNode } from \"./ast.mjs\";\n\nexport class Parser {\n private tokens: Token[];\n private pos: number = 0;\n\n constructor(tokens: Token[]) {\n this.tokens = tokens;\n }\n\n parse(): ASTNode {\n const result = this.parseSequence();\n if (!this.isAtEnd()) {\n throw new ParseError(`Unexpected token: ${JSON.stringify(this.peek())}`);\n }\n return result;\n }\n\n // sequence := and_or (';' and_or)*\n private parseSequence(): ASTNode {\n const commands: ASTNode[] = [];\n commands.push(this.parseAndOr());\n\n while (this.match(\"semicolon\")) {\n // Skip empty commands after semicolon\n if (this.isAtEnd() || this.check(\"semicolon\")) continue;\n commands.push(this.parseAndOr());\n }\n\n if (commands.length === 1) {\n return commands[0]!;\n }\n\n return { type: \"sequence\", commands };\n }\n\n // and_or := pipeline (('&&'|'||') pipeline)*\n private parseAndOr(): ASTNode {\n let left = this.parsePipeline();\n\n while (this.check(\"and\") || this.check(\"or\")) {\n if (this.match(\"and\")) {\n const right = this.parsePipeline();\n left = { type: \"and\", left, right };\n } else if (this.match(\"or\")) {\n const right = this.parsePipeline();\n left = { type: \"or\", left, right };\n }\n }\n\n return left;\n }\n\n // pipeline := command ('|' command)*\n private parsePipeline(): ASTNode {\n const commands: ASTNode[] = [];\n commands.push(this.parseCommand());\n\n while (this.match(\"pipe\")) {\n commands.push(this.parseCommand());\n }\n\n if (commands.length === 1) {\n return commands[0]!;\n }\n\n return { type: \"pipeline\", commands };\n }\n\n // command := assignment* word+ redirect*\n private parseCommand(): CommandNode {\n const assignments: Array<{ name: string; value: ASTNode }> = [];\n const args: ASTNode[] = [];\n const redirects: Redirect[] = [];\n\n // Collect leading assignments\n while (this.check(\"assignment\")) {\n const token = this.advance() as Token & { type: \"assignment\" };\n assignments.push({\n name: token.name,\n value: this.tokenToNode(token.value),\n });\n }\n\n // Collect command name and arguments\n while (this.isWordToken()) {\n args.push(this.parseWordArg());\n }\n\n // Collect redirects\n while (this.check(\"redirect\")) {\n const redirect = this.parseRedirect();\n redirects.push(redirect);\n // After a redirect, there might be more words\n while (this.isWordToken()) {\n args.push(this.parseWordArg());\n }\n }\n\n if (args.length === 0 && assignments.length === 0) {\n throw new ParseError(\"Expected command\");\n }\n\n const name = args.shift() ?? { type: \"literal\" as const, value: \"\" };\n\n return {\n type: \"command\",\n name,\n args,\n redirects,\n assignments,\n };\n }\n\n private parseWordArg(): ASTNode {\n const token = this.advance();\n return this.tokenToNode(token);\n }\n\n private tokenToNode(token: Token | string | Token[]): ASTNode {\n if (typeof token === \"string\") {\n return { type: \"literal\", value: token };\n }\n\n if (Array.isArray(token)) {\n const parts = token.map((t) => this.tokenToNode(t));\n if (parts.length === 1) return parts[0]!;\n return { type: \"concat\", parts };\n }\n\n switch (token.type) {\n case \"word\":\n return { type: \"literal\", value: token.value };\n case \"singleQuote\":\n return { type: \"literal\", value: token.value };\n case \"doubleQuote\":\n return this.parseDoubleQuoteParts(token.parts);\n case \"variable\":\n return { type: \"variable\", name: token.name };\n case \"substitution\":\n // Parse the inner command\n const innerParser = new Parser(\n new (require(\"../lexer/lexer.ts\").Lexer)(token.command).tokenize()\n );\n return { type: \"substitution\", command: innerParser.parse() };\n case \"glob\":\n return { type: \"glob\", pattern: token.pattern };\n case \"assignment\":\n return this.tokenToNode(token.value);\n default:\n throw new ParseError(`Unexpected token type: ${token.type}`);\n }\n }\n\n private parseDoubleQuoteParts(parts: Array<string | Token>): ASTNode {\n if (parts.length === 0) {\n return { type: \"literal\", value: \"\" };\n }\n\n if (parts.length === 1) {\n const part = parts[0]!;\n if (typeof part === \"string\") {\n return { type: \"literal\", value: part };\n }\n return this.tokenToNode(part);\n }\n\n const nodes: ASTNode[] = parts.map((part) => {\n if (typeof part === \"string\") {\n return { type: \"literal\" as const, value: part };\n }\n return this.tokenToNode(part);\n });\n\n return { type: \"concat\", parts: nodes };\n }\n\n private parseRedirect(): Redirect {\n const token = this.advance() as Token & { type: \"redirect\" };\n const mode = token.mode as RedirectMode;\n\n // 2>&1 and 1>&2 don't have a target\n if (mode === \"2>&1\" || mode === \"1>&2\") {\n return { mode, target: { type: \"literal\", value: \"\" } };\n }\n\n if (!this.isWordToken()) {\n throw new ParseError(`Expected redirect target after ${mode}`);\n }\n\n const target = this.parseWordArg();\n return { mode, target };\n }\n\n private isWordToken(): boolean {\n const token = this.peek();\n return (\n token.type === \"word\" ||\n token.type === \"singleQuote\" ||\n token.type === \"doubleQuote\" ||\n token.type === \"variable\" ||\n token.type === \"substitution\" ||\n token.type === \"glob\"\n );\n }\n\n private check(type: Token[\"type\"]): boolean {\n return this.peek().type === type;\n }\n\n private match(type: Token[\"type\"]): boolean {\n if (this.check(type)) {\n this.advance();\n return true;\n }\n return false;\n }\n\n private peek(): Token {\n return this.tokens[this.pos] ?? { type: \"eof\" };\n }\n\n private advance(): Token {\n const token = this.peek();\n this.pos++;\n return token;\n }\n\n private isAtEnd(): boolean {\n return this.peek().type === \"eof\";\n }\n}\n\nexport function parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n}\n"
|
|
5
|
+
"import { ParseError } from \"../errors.mjs\";\nimport type { Token, KeywordValue } from \"../lexer/tokens.mjs\";\nimport type { ASTNode, Redirect, RedirectMode, CommandNode, IfNode, ForNode, WhileNode, UntilNode, CaseNode, CaseClause } from \"./ast.mjs\";\n\nexport class Parser {\n private tokens: Token[];\n private pos: number = 0;\n\n constructor(tokens: Token[]) {\n this.tokens = tokens;\n }\n\n parse(): ASTNode {\n const result = this.parseSequence();\n if (!this.isAtEnd()) {\n throw new ParseError(`Unexpected token: ${JSON.stringify(this.peek())}`);\n }\n return result;\n }\n\n // sequence := and_or ((';'|'\\n') and_or)*\n private parseSequence(): ASTNode {\n this.skipNewlines();\n const commands: ASTNode[] = [];\n commands.push(this.parseAndOr());\n\n while (this.match(\"semicolon\") || this.match(\"newline\")) {\n this.skipNewlines();\n // Skip empty commands after separator, or stop at terminating keywords\n if (this.isAtEnd() || this.check(\"semicolon\") || this.check(\"newline\") || this.isTerminatingKeyword()) continue;\n commands.push(this.parseAndOr());\n }\n\n if (commands.length === 1) {\n return commands[0]!;\n }\n\n return { type: \"sequence\", commands };\n }\n\n private skipNewlines(): void {\n while (this.match(\"newline\")) {\n // keep consuming newlines\n }\n }\n\n private isTerminatingKeyword(): boolean {\n const token = this.peek();\n if (token.type !== \"keyword\") return false;\n return [\"then\", \"elif\", \"else\", \"fi\", \"do\", \"done\", \"esac\"].includes(token.value);\n }\n\n // and_or := pipeline (('&&'|'||') pipeline)*\n private parseAndOr(): ASTNode {\n let left = this.parsePipeline();\n\n while (this.check(\"and\") || this.check(\"or\")) {\n if (this.match(\"and\")) {\n const right = this.parsePipeline();\n left = { type: \"and\", left, right };\n } else if (this.match(\"or\")) {\n const right = this.parsePipeline();\n left = { type: \"or\", left, right };\n }\n }\n\n return left;\n }\n\n // pipeline := command ('|' command)*\n private parsePipeline(): ASTNode {\n const commands: ASTNode[] = [];\n commands.push(this.parseCompoundOrCommand());\n\n while (this.match(\"pipe\")) {\n this.skipNewlines();\n commands.push(this.parseCompoundOrCommand());\n }\n\n if (commands.length === 1) {\n return commands[0]!;\n }\n\n return { type: \"pipeline\", commands };\n }\n\n // compound_or_command := compound_command | simple_command\n private parseCompoundOrCommand(): ASTNode {\n this.skipNewlines();\n const token = this.peek();\n\n if (token.type === \"keyword\") {\n switch (token.value) {\n case \"if\":\n return this.parseIf();\n case \"for\":\n return this.parseFor();\n case \"while\":\n return this.parseWhile();\n case \"until\":\n return this.parseUntil();\n case \"case\":\n return this.parseCase();\n }\n }\n\n return this.parseCommand();\n }\n\n // if := 'if' compound_list 'then' compound_list ('elif' compound_list 'then' compound_list)* ['else' compound_list] 'fi'\n private parseIf(): IfNode {\n this.expectKeyword(\"if\");\n const condition = this.parseCompoundList([\"then\"]);\n this.expectKeyword(\"then\");\n const thenBranch = this.parseCompoundList([\"elif\", \"else\", \"fi\"]);\n\n const elifBranches: Array<{ condition: ASTNode; body: ASTNode }> = [];\n while (this.checkKeyword(\"elif\")) {\n this.expectKeyword(\"elif\");\n const elifCondition = this.parseCompoundList([\"then\"]);\n this.expectKeyword(\"then\");\n const elifBody = this.parseCompoundList([\"elif\", \"else\", \"fi\"]);\n elifBranches.push({ condition: elifCondition, body: elifBody });\n }\n\n let elseBranch: ASTNode | undefined;\n if (this.checkKeyword(\"else\")) {\n this.expectKeyword(\"else\");\n elseBranch = this.parseCompoundList([\"fi\"]);\n }\n\n this.expectKeyword(\"fi\");\n\n return {\n type: \"if\",\n condition,\n thenBranch,\n elifBranches,\n elseBranch,\n };\n }\n\n // for := 'for' NAME ['in' word*] (';'|'\\n') 'do' compound_list 'done'\n private parseFor(): ForNode {\n this.expectKeyword(\"for\");\n\n // Get variable name\n const varToken = this.peek();\n if (varToken.type !== \"word\") {\n throw new ParseError(\"Expected variable name after 'for'\");\n }\n this.advance();\n const variable = varToken.value;\n\n // Parse optional 'in' clause\n const items: ASTNode[] = [];\n if (this.checkKeyword(\"in\")) {\n this.expectKeyword(\"in\");\n // Read items until semicolon, newline, or 'do'\n while (!this.isAtEnd() && !this.check(\"semicolon\") && !this.check(\"newline\") && !this.checkKeyword(\"do\")) {\n items.push(this.parseWordArg());\n }\n }\n\n // Consume separator (semicolon or newline)\n if (!this.match(\"semicolon\")) {\n this.match(\"newline\");\n }\n this.skipNewlines();\n\n this.expectKeyword(\"do\");\n const body = this.parseCompoundList([\"done\"]);\n this.expectKeyword(\"done\");\n\n return {\n type: \"for\",\n variable,\n items,\n body,\n };\n }\n\n // while := 'while' compound_list 'do' compound_list 'done'\n private parseWhile(): WhileNode {\n this.expectKeyword(\"while\");\n const condition = this.parseCompoundList([\"do\"]);\n this.expectKeyword(\"do\");\n const body = this.parseCompoundList([\"done\"]);\n this.expectKeyword(\"done\");\n\n return {\n type: \"while\",\n condition,\n body,\n };\n }\n\n // until := 'until' compound_list 'do' compound_list 'done'\n private parseUntil(): UntilNode {\n this.expectKeyword(\"until\");\n const condition = this.parseCompoundList([\"do\"]);\n this.expectKeyword(\"do\");\n const body = this.parseCompoundList([\"done\"]);\n this.expectKeyword(\"done\");\n\n return {\n type: \"until\",\n condition,\n body,\n };\n }\n\n // case := 'case' word 'in' (pattern ('|' pattern)* ')' compound_list ';;')* 'esac'\n private parseCase(): CaseNode {\n this.expectKeyword(\"case\");\n const word = this.parseWordArg();\n this.expectKeyword(\"in\");\n this.skipNewlines();\n\n const clauses: CaseClause[] = [];\n\n while (!this.isAtEnd() && !this.checkKeyword(\"esac\")) {\n // Parse patterns separated by '|'\n // Skip leading '(' if present\n this.match(\"openParen\");\n\n const patterns: ASTNode[] = [];\n patterns.push(this.parseCasePattern());\n\n while (this.match(\"pipe\")) {\n patterns.push(this.parseCasePattern());\n }\n\n // Expect ')'\n if (!this.match(\"closeParen\")) {\n throw new ParseError(\"Expected ')' after case pattern\");\n }\n\n // Parse body until ';;' or 'esac'\n const body = this.parseCaseBody();\n\n clauses.push({ patterns, body });\n\n // ';;' is optional for the last clause\n this.match(\"doubleSemicolon\");\n this.skipNewlines();\n }\n\n this.expectKeyword(\"esac\");\n\n return {\n type: \"case\",\n word,\n clauses,\n };\n }\n\n private parseCasePattern(): ASTNode {\n const token = this.peek();\n\n // Handle glob patterns and words\n if (token.type === \"word\" || token.type === \"glob\" || token.type === \"singleQuote\" || token.type === \"doubleQuote\") {\n return this.parseWordArg();\n }\n\n throw new ParseError(`Expected pattern in case clause, got ${token.type}`);\n }\n\n private parseCaseBody(): ASTNode {\n const commands: ASTNode[] = [];\n this.skipNewlines();\n\n // Parse until ';;' or 'esac'\n while (!this.isAtEnd() && !this.check(\"doubleSemicolon\") && !this.checkKeyword(\"esac\")) {\n commands.push(this.parseAndOr());\n // Consume separator\n if (!this.match(\"semicolon\") && !this.match(\"newline\")) {\n break;\n }\n this.skipNewlines();\n // Check for terminators after consuming separators\n if (this.check(\"doubleSemicolon\") || this.checkKeyword(\"esac\")) {\n break;\n }\n }\n\n if (commands.length === 0) {\n return { type: \"command\", name: { type: \"literal\", value: \"true\" }, args: [], redirects: [], assignments: [] };\n }\n if (commands.length === 1) {\n return commands[0]!;\n }\n return { type: \"sequence\", commands };\n }\n\n // compound_list := and_or ((';'|'\\n') and_or)* [';'|'\\n']\n private parseCompoundList(terminators: KeywordValue[]): ASTNode {\n this.skipNewlines();\n const commands: ASTNode[] = [];\n\n // Check for empty list\n if (this.isCompoundListTerminator(terminators)) {\n // Return a no-op command for empty list\n return { type: \"command\", name: { type: \"literal\", value: \"true\" }, args: [], redirects: [], assignments: [] };\n }\n\n commands.push(this.parseAndOr());\n\n while ((this.match(\"semicolon\") || this.match(\"newline\")) && !this.isAtEnd()) {\n this.skipNewlines();\n // Check for terminators\n if (this.isCompoundListTerminator(terminators)) {\n break;\n }\n commands.push(this.parseAndOr());\n }\n\n if (commands.length === 1) {\n return commands[0]!;\n }\n\n return { type: \"sequence\", commands };\n }\n\n private isCompoundListTerminator(terminators: KeywordValue[]): boolean {\n const token = this.peek();\n if (token.type !== \"keyword\") return false;\n return terminators.includes(token.value);\n }\n\n private checkKeyword(value: KeywordValue): boolean {\n const token = this.peek();\n return token.type === \"keyword\" && token.value === value;\n }\n\n private expectKeyword(value: KeywordValue): void {\n if (!this.checkKeyword(value)) {\n throw new ParseError(`Expected '${value}'`);\n }\n this.advance();\n // Don't skip newlines here - let the caller handle them\n // Newlines are significant as command separators in compound lists\n }\n\n // command := assignment* word+ redirect*\n private parseCommand(): CommandNode {\n const assignments: Array<{ name: string; value: ASTNode }> = [];\n const args: ASTNode[] = [];\n const redirects: Redirect[] = [];\n\n // Collect leading assignments\n while (this.check(\"assignment\")) {\n const token = this.advance() as Token & { type: \"assignment\" };\n assignments.push({\n name: token.name,\n value: this.tokenToNode(token.value),\n });\n }\n\n // Collect command name and arguments\n while (this.isWordToken()) {\n // Check if it's a heredoc token - convert to input redirect\n if (this.peek().type === \"heredoc\") {\n const heredocToken = this.advance() as Token & { type: \"heredoc\" };\n redirects.push({\n mode: \"<\",\n target: this.tokenToNode(heredocToken),\n heredocContent: true,\n });\n } else {\n args.push(this.parseWordArg());\n }\n }\n\n // Collect redirects\n while (this.check(\"redirect\")) {\n const redirect = this.parseRedirect();\n redirects.push(redirect);\n // After a redirect, there might be more words\n while (this.isWordToken()) {\n args.push(this.parseWordArg());\n }\n }\n\n if (args.length === 0 && assignments.length === 0) {\n throw new ParseError(\"Expected command\");\n }\n\n const name = args.shift() ?? { type: \"literal\" as const, value: \"\" };\n\n return {\n type: \"command\",\n name,\n args,\n redirects,\n assignments,\n };\n }\n\n private parseWordArg(): ASTNode {\n const token = this.advance();\n return this.tokenToNode(token);\n }\n\n private tokenToNode(token: Token | string | Token[]): ASTNode {\n if (typeof token === \"string\") {\n return { type: \"literal\", value: token };\n }\n\n if (Array.isArray(token)) {\n const parts = token.map((t) => this.tokenToNode(t));\n if (parts.length === 1) return parts[0]!;\n return { type: \"concat\", parts };\n }\n\n switch (token.type) {\n case \"word\":\n return { type: \"literal\", value: token.value };\n case \"singleQuote\":\n return { type: \"literal\", value: token.value };\n case \"doubleQuote\":\n return this.parseDoubleQuoteParts(token.parts);\n case \"variable\":\n return { type: \"variable\", name: token.name };\n case \"substitution\":\n // Parse the inner command\n const innerParser = new Parser(\n new (require(\"../lexer/lexer.ts\").Lexer)(token.command, { preserveNewlines: true }).tokenize()\n );\n return { type: \"substitution\", command: innerParser.parse() };\n case \"arithmetic\":\n return { type: \"arithmetic\", expression: token.expression };\n case \"glob\":\n return { type: \"glob\", pattern: token.pattern };\n case \"assignment\":\n return this.tokenToNode(token.value);\n case \"heredoc\":\n if (token.expand) {\n return this.parseHeredocContent(token.content);\n }\n return { type: \"literal\", value: token.content };\n default:\n throw new ParseError(`Unexpected token type: ${(token as Token).type}`);\n }\n }\n\n private parseDoubleQuoteParts(parts: Array<string | Token>): ASTNode {\n if (parts.length === 0) {\n return { type: \"literal\", value: \"\" };\n }\n\n if (parts.length === 1) {\n const part = parts[0]!;\n if (typeof part === \"string\") {\n return { type: \"literal\", value: part };\n }\n return this.tokenToNode(part);\n }\n\n const nodes: ASTNode[] = parts.map((part) => {\n if (typeof part === \"string\") {\n return { type: \"literal\" as const, value: part };\n }\n return this.tokenToNode(part);\n });\n\n return { type: \"concat\", parts: nodes };\n }\n\n private parseHeredocContent(content: string): ASTNode {\n // Parse content looking for $VAR and ${VAR} patterns\n const parts: ASTNode[] = [];\n let currentLiteral = \"\";\n let i = 0;\n\n while (i < content.length) {\n if (content[i] === \"$\") {\n // Flush current literal\n if (currentLiteral) {\n parts.push({ type: \"literal\", value: currentLiteral });\n currentLiteral = \"\";\n }\n\n i++; // consume $\n if (i >= content.length) {\n currentLiteral += \"$\";\n break;\n }\n\n if (content[i] === \"{\") {\n // ${VAR} syntax\n i++; // consume {\n let varName = \"\";\n while (i < content.length && content[i] !== \"}\") {\n varName += content[i];\n i++;\n }\n if (i < content.length && content[i] === \"}\") {\n i++; // consume }\n }\n if (varName) {\n parts.push({ type: \"variable\", name: varName });\n }\n } else if (/[a-zA-Z_]/.test(content[i] ?? \"\")) {\n // $VAR syntax\n let varName = \"\";\n while (i < content.length && /[a-zA-Z0-9_]/.test(content[i] ?? \"\")) {\n varName += content[i];\n i++;\n }\n parts.push({ type: \"variable\", name: varName });\n } else {\n // Not a variable, keep the $\n currentLiteral += \"$\";\n }\n } else {\n currentLiteral += content[i];\n i++;\n }\n }\n\n // Flush remaining literal\n if (currentLiteral) {\n parts.push({ type: \"literal\", value: currentLiteral });\n }\n\n if (parts.length === 0) {\n return { type: \"literal\", value: \"\" };\n }\n if (parts.length === 1) {\n return parts[0]!;\n }\n return { type: \"concat\", parts };\n }\n\n private parseRedirect(): Redirect {\n const token = this.advance() as Token & { type: \"redirect\" };\n const mode = token.mode as RedirectMode;\n\n // 2>&1 and 1>&2 don't have a target\n if (mode === \"2>&1\" || mode === \"1>&2\") {\n return { mode, target: { type: \"literal\", value: \"\" } };\n }\n\n if (!this.isWordToken()) {\n throw new ParseError(`Expected redirect target after ${mode}`);\n }\n\n const target = this.parseWordArg();\n return { mode, target };\n }\n\n private isWordToken(): boolean {\n const token = this.peek();\n return (\n token.type === \"word\" ||\n token.type === \"singleQuote\" ||\n token.type === \"doubleQuote\" ||\n token.type === \"variable\" ||\n token.type === \"substitution\" ||\n token.type === \"arithmetic\" ||\n token.type === \"glob\" ||\n token.type === \"heredoc\"\n );\n }\n\n private check(type: Token[\"type\"]): boolean {\n return this.peek().type === type;\n }\n\n private match(type: Token[\"type\"]): boolean {\n if (this.check(type)) {\n this.advance();\n return true;\n }\n return false;\n }\n\n private peek(): Token {\n return this.tokens[this.pos] ?? { type: \"eof\" };\n }\n\n private advance(): Token {\n const token = this.peek();\n this.pos++;\n return token;\n }\n\n private isAtEnd(): boolean {\n return this.peek().type === \"eof\";\n }\n}\n\nexport function parse(tokens: Token[]): ASTNode {\n return new Parser(tokens).parse();\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": "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",
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": "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",
|
|
8
|
+
"debugId": "D4EE93E6428C592D64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -100,7 +100,7 @@ class ShellDSL {
|
|
|
100
100
|
return escape(str);
|
|
101
101
|
}
|
|
102
102
|
lex(source) {
|
|
103
|
-
return new Lexer(source).tokenize();
|
|
103
|
+
return new Lexer(source, { preserveNewlines: true }).tokenize();
|
|
104
104
|
}
|
|
105
105
|
parse(tokens) {
|
|
106
106
|
return new Parser(tokens).parse();
|
|
@@ -154,4 +154,4 @@ export {
|
|
|
154
154
|
ShellDSL
|
|
155
155
|
};
|
|
156
156
|
|
|
157
|
-
//# debugId=
|
|
157
|
+
//# debugId=7846EA9AA40AD9AA64756E2164756E21
|