* Parse a sequence of statements separated by ; & newline. Returns a flat list * where ; and & are sibling leaves (NOT wrapped in 'list' — only && || get * that). Stops at terminator or EOF.
(P: ParseState, terminator: string | null)
| 767 | * that). Stops at terminator or EOF. |
| 768 | */ |
| 769 | function parseStatements(P: ParseState, terminator: string | null): TsNode[] { |
| 770 | const out: TsNode[] = [] |
| 771 | while (true) { |
| 772 | skipBlanks(P.L) |
| 773 | const save = saveLex(P.L) |
| 774 | const t = nextToken(P.L, 'cmd') |
| 775 | if (t.type === 'EOF') { |
| 776 | restoreLex(P.L, save) |
| 777 | break |
| 778 | } |
| 779 | if (t.type === 'NEWLINE') { |
| 780 | // Process pending heredocs |
| 781 | if (P.L.heredocs.length > 0) { |
| 782 | scanHeredocBodies(P) |
| 783 | } |
| 784 | continue |
| 785 | } |
| 786 | if (t.type === 'COMMENT') { |
| 787 | out.push(leaf(P, 'comment', t)) |
| 788 | continue |
| 789 | } |
| 790 | if (terminator && t.type === 'OP' && t.value === terminator) { |
| 791 | restoreLex(P.L, save) |
| 792 | break |
| 793 | } |
| 794 | if ( |
| 795 | t.type === 'OP' && |
| 796 | (t.value === ')' || |
| 797 | t.value === '}' || |
| 798 | t.value === ';;' || |
| 799 | t.value === ';&' || |
| 800 | t.value === ';;&' || |
| 801 | t.value === '))' || |
| 802 | t.value === ']]' || |
| 803 | t.value === ']') |
| 804 | ) { |
| 805 | restoreLex(P.L, save) |
| 806 | break |
| 807 | } |
| 808 | if (t.type === 'BACKTICK' && P.inBacktick > 0) { |
| 809 | restoreLex(P.L, save) |
| 810 | break |
| 811 | } |
| 812 | if ( |
| 813 | t.type === 'WORD' && |
| 814 | (t.value === 'then' || |
| 815 | t.value === 'elif' || |
| 816 | t.value === 'else' || |
| 817 | t.value === 'fi' || |
| 818 | t.value === 'do' || |
| 819 | t.value === 'done' || |
| 820 | t.value === 'esac') |
| 821 | ) { |
| 822 | restoreLex(P.L, save) |
| 823 | break |
| 824 | } |
| 825 | restoreLex(P.L, save) |
| 826 | const stmt = parseAndOr(P) |
no test coverage detected