(P: ParseState)
| 3034 | // repeat(choice(regex, string, raw_string, ')', /\s+/→regex)). Each quote |
| 3035 | // becomes a SIBLING node, not absorbed. `${f%'str'*}` → (raw_string)(regex). |
| 3036 | function parseExpansionRegexSegmented(P: ParseState): TsNode[] { |
| 3037 | const out: TsNode[] = [] |
| 3038 | let segStart = P.L.b |
| 3039 | const flushRegex = (): void => { |
| 3040 | if (P.L.b > segStart) out.push(mk(P, 'regex', segStart, P.L.b, [])) |
| 3041 | } |
| 3042 | while (P.L.i < P.L.len) { |
| 3043 | const c = peek(P.L) |
| 3044 | if (c === '}' || c === '\n') break |
| 3045 | if (c === '\\' && P.L.i + 1 < P.L.len) { |
| 3046 | advance(P.L) |
| 3047 | advance(P.L) |
| 3048 | continue |
| 3049 | } |
| 3050 | if (c === '"') { |
| 3051 | flushRegex() |
| 3052 | out.push(parseDoubleQuoted(P)) |
| 3053 | segStart = P.L.b |
| 3054 | continue |
| 3055 | } |
| 3056 | if (c === "'") { |
| 3057 | flushRegex() |
| 3058 | const rStart = P.L.b |
| 3059 | advance(P.L) |
| 3060 | while (P.L.i < P.L.len && peek(P.L) !== "'") advance(P.L) |
| 3061 | if (peek(P.L) === "'") advance(P.L) |
| 3062 | out.push(mk(P, 'raw_string', rStart, P.L.b, [])) |
| 3063 | segStart = P.L.b |
| 3064 | continue |
| 3065 | } |
| 3066 | // Nested ${...} $(...) — opaque scan so their } doesn't terminate us |
| 3067 | if (c === '$') { |
| 3068 | const c1 = peek(P.L, 1) |
| 3069 | if (c1 === '{') { |
| 3070 | let d = 1 |
| 3071 | advance(P.L) |
| 3072 | advance(P.L) |
| 3073 | while (P.L.i < P.L.len && d > 0) { |
| 3074 | const nc = peek(P.L) |
| 3075 | if (nc === '{') d++ |
| 3076 | else if (nc === '}') d-- |
| 3077 | advance(P.L) |
| 3078 | } |
| 3079 | continue |
| 3080 | } |
| 3081 | if (c1 === '(') { |
| 3082 | let d = 1 |
| 3083 | advance(P.L) |
| 3084 | advance(P.L) |
| 3085 | while (P.L.i < P.L.len && d > 0) { |
| 3086 | const nc = peek(P.L) |
| 3087 | if (nc === '(') d++ |
| 3088 | else if (nc === ')') d-- |
| 3089 | advance(P.L) |
| 3090 | } |
| 3091 | continue |
| 3092 | } |
| 3093 | } |
no test coverage detected