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