(P: ParseState)
| 3099 | } |
| 3100 | |
| 3101 | function parseBacktick(P: ParseState): TsNode | null { |
| 3102 | const start = P.L.b |
| 3103 | advance(P.L) |
| 3104 | const open = mk(P, '`', start, P.L.b, []) |
| 3105 | P.inBacktick++ |
| 3106 | // Parse statements inline — stop at closing backtick |
| 3107 | const body: TsNode[] = [] |
| 3108 | while (true) { |
| 3109 | skipBlanks(P.L) |
| 3110 | if (peek(P.L) === '`' || peek(P.L) === '') break |
| 3111 | const save = saveLex(P.L) |
| 3112 | const t = nextToken(P.L, 'cmd') |
| 3113 | if (t.type === 'EOF' || t.type === 'BACKTICK') { |
| 3114 | restoreLex(P.L, save) |
| 3115 | break |
| 3116 | } |
| 3117 | if (t.type === 'NEWLINE') continue |
| 3118 | restoreLex(P.L, save) |
| 3119 | const stmt = parseAndOr(P) |
| 3120 | if (!stmt) break |
| 3121 | body.push(stmt) |
| 3122 | skipBlanks(P.L) |
| 3123 | if (peek(P.L) === '`') break |
| 3124 | const save2 = saveLex(P.L) |
| 3125 | const sep = nextToken(P.L, 'cmd') |
| 3126 | if (sep.type === 'OP' && (sep.value === ';' || sep.value === '&')) { |
| 3127 | body.push(leaf(P, sep.value, sep)) |
| 3128 | } else if (sep.type !== 'NEWLINE') { |
| 3129 | restoreLex(P.L, save2) |
| 3130 | } |
| 3131 | } |
| 3132 | P.inBacktick-- |
| 3133 | let close: TsNode |
| 3134 | if (peek(P.L) === '`') { |
| 3135 | const cStart = P.L.b |
| 3136 | advance(P.L) |
| 3137 | close = mk(P, '`', cStart, P.L.b, []) |
| 3138 | } else { |
| 3139 | close = mk(P, '`', P.L.b, P.L.b, []) |
| 3140 | } |
| 3141 | // Empty backticks (whitespace/newline only) are elided entirely by |
| 3142 | // tree-sitter — used as a line-continuation hack: "foo"`<newline>`"bar" |
| 3143 | // → (concatenation (string) (string)) with no command_substitution. |
| 3144 | if (body.length === 0) return null |
| 3145 | return mk(P, 'command_substitution', start, close.endIndex, [ |
| 3146 | open, |
| 3147 | ...body, |
| 3148 | close, |
| 3149 | ]) |
| 3150 | } |
| 3151 | |
| 3152 | function parseIf(P: ParseState, ifTok: Token): TsNode { |
| 3153 | const ifKw = leaf(P, 'if', ifTok) |
no test coverage detected