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