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