* Parse commands joined by | or |&. Flat children with operator leaves. * tree-sitter quirk: `a | b 2>nul | c` hoists the redirect on `b` to wrap * the preceding pipeline fragment — pipeline(redirected_statement( * pipeline(a,|,b), 2>nul), |, c).
(P: ParseState)
| 932 | * pipeline(a,|,b), 2>nul), |, c). |
| 933 | */ |
| 934 | function parsePipeline(P: ParseState): TsNode | null { |
| 935 | let first = parseCommand(P) |
| 936 | if (!first) return null |
| 937 | const parts: TsNode[] = [first] |
| 938 | while (true) { |
| 939 | const save = saveLex(P.L) |
| 940 | const t = nextToken(P.L, 'cmd') |
| 941 | if (t.type === 'OP' && (t.value === '|' || t.value === '|&')) { |
| 942 | const op = leaf(P, t.value, t) |
| 943 | skipNewlines(P) |
| 944 | const next = parseCommand(P) |
| 945 | if (!next) { |
| 946 | parts.push(op) |
| 947 | break |
| 948 | } |
| 949 | // Hoist trailing redirect on `next` to wrap current pipeline fragment |
| 950 | if ( |
| 951 | next.type === 'redirected_statement' && |
| 952 | next.children.length >= 2 && |
| 953 | parts.length >= 1 |
| 954 | ) { |
| 955 | const inner = next.children[0]! |
| 956 | const redirs = next.children.slice(1) |
| 957 | // Wrap existing parts + op + inner as a pipeline |
| 958 | const pipeKids = [...parts, op, inner] |
| 959 | const pipeNode = mk( |
| 960 | P, |
| 961 | 'pipeline', |
| 962 | pipeKids[0]!.startIndex, |
| 963 | inner.endIndex, |
| 964 | pipeKids, |
| 965 | ) |
| 966 | const lastR = redirs[redirs.length - 1]! |
| 967 | const wrapped = mk( |
| 968 | P, |
| 969 | 'redirected_statement', |
| 970 | pipeNode.startIndex, |
| 971 | lastR.endIndex, |
| 972 | [pipeNode, ...redirs], |
| 973 | ) |
| 974 | parts.length = 0 |
| 975 | parts.push(wrapped) |
| 976 | first = wrapped |
| 977 | continue |
| 978 | } |
| 979 | parts.push(op, next) |
| 980 | } else { |
| 981 | restoreLex(P.L, save) |
| 982 | break |
| 983 | } |
| 984 | } |
| 985 | if (parts.length === 1) return parts[0]! |
| 986 | const last = parts[parts.length - 1]! |
| 987 | return mk(P, 'pipeline', parts[0]!.startIndex, last.endIndex, parts) |
| 988 | } |
| 989 | |
| 990 | /** Parse a single command: simple, compound, or control structure. */ |
| 991 | function parseCommand(P: ParseState): TsNode | null { |
no test coverage detected