(P: ParseState)
| 3935 | // RHS of =~ in [[ ]] — scan as single (regex) node with paren/bracket counting |
| 3936 | // so | ( ) inside the regex don't break parsing. Stop at ]] or ws+&&/||. |
| 3937 | function parseTestRegexRhs(P: ParseState): TsNode | null { |
| 3938 | skipBlanks(P.L) |
| 3939 | const start = P.L.b |
| 3940 | let parenDepth = 0 |
| 3941 | let bracketDepth = 0 |
| 3942 | while (P.L.i < P.L.len) { |
| 3943 | const c = peek(P.L) |
| 3944 | if (c === '\\' && P.L.i + 1 < P.L.len) { |
| 3945 | advance(P.L) |
| 3946 | advance(P.L) |
| 3947 | continue |
| 3948 | } |
| 3949 | if (c === '\n') break |
| 3950 | if (parenDepth === 0 && bracketDepth === 0) { |
| 3951 | if (c === ']' && peek(P.L, 1) === ']') break |
| 3952 | if (c === ' ' || c === '\t') { |
| 3953 | // Peek past blanks for ]] or &&/|| |
| 3954 | let j = P.L.i |
| 3955 | while (j < P.L.len && (P.L.src[j] === ' ' || P.L.src[j] === '\t')) j++ |
| 3956 | const nc = P.L.src[j] ?? '' |
| 3957 | const nc1 = P.L.src[j + 1] ?? '' |
| 3958 | if ( |
| 3959 | (nc === ']' && nc1 === ']') || |
| 3960 | (nc === '&' && nc1 === '&') || |
| 3961 | (nc === '|' && nc1 === '|') |
| 3962 | ) { |
| 3963 | break |
| 3964 | } |
| 3965 | advance(P.L) |
| 3966 | continue |
| 3967 | } |
| 3968 | } |
| 3969 | if (c === '(') parenDepth++ |
| 3970 | else if (c === ')' && parenDepth > 0) parenDepth-- |
| 3971 | else if (c === '[') bracketDepth++ |
| 3972 | else if (c === ']' && bracketDepth > 0) bracketDepth-- |
| 3973 | advance(P.L) |
| 3974 | } |
| 3975 | if (P.L.b === start) return null |
| 3976 | return mk(P, 'regex', start, P.L.b, []) |
| 3977 | } |
| 3978 | |
| 3979 | // RHS of ==/!=/= in [[ ]] — returns array of parts. Bare text → extglob_pattern |
| 3980 | // (with paren counting for @(a|b)); $(...)/${}/quoted → proper node types. |
no test coverage detected