[
https://issues.apache.org/jira/browse/GROOVY-12133?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18095735#comment-18095735
]
ASF GitHub Bot commented on GROOVY-12133:
-----------------------------------------
sonarqubecloud[bot] commented on PR #2669:
URL: https://github.com/apache/groovy/pull/2669#issuecomment-4952410561
## [](https://sonarcloud.io/dashboard?id=apache_groovy&pullRequest=2669)
**Quality Gate passed**
Issues
 [3 New
issues](https://sonarcloud.io/project/issues?id=apache_groovy&pullRequest=2669&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)
 [0 Accepted
issues](https://sonarcloud.io/project/issues?id=apache_groovy&pullRequest=2669&issueStatuses=ACCEPTED)
Measures
 [0 Security
Hotspots](https://sonarcloud.io/project/security_hotspots?id=apache_groovy&pullRequest=2669&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)
 [97.4% Coverage on New
Code](https://sonarcloud.io/component_measures?id=apache_groovy&pullRequest=2669&metric=new_coverage&view=list)
 [0.0% Duplication on New
Code](https://sonarcloud.io/component_measures?id=apache_groovy&pullRequest=2669&metric=new_duplicated_lines_density&view=list)
<!
> Implement findBalancedGroups utility to support Regex Balanced Groups
> ---------------------------------------------------------------------
>
> Key: GROOVY-12133
> URL: https://issues.apache.org/jira/browse/GROOVY-12133
> Project: Groovy
> Issue Type: Improvement
> Reporter: Daniel Sun
> Priority: Major
> Fix For: 6.0.0-beta-1
>
>
> h2. Summary
> Add a GDK utility to extract nested balanced groups from text (parentheses,
> braces, HTML-like tags, etc.), providing a structured alternative to .NET
> regex balancing groups for the Java/Groovy regex stack.
> h2. Motivation
> {{java.util.regex.Pattern}} has no equivalent of .NET balancing groups
> ({{{}(?<name1-name2>…){}}}). Nested constructs are therefore awkward to
> handle with pure Java regex and often lead to fragile or backtracking-heavy
> patterns. Groovy users need a first-class, safe way to recover hierarchical
> balanced spans with clear source offsets.
> h2. Solution
> Introduce {{groovy.util.regex.BalancedGroup}} and a GDK method
> {{{}CharSequence.findBalancedGroups(...){}}}, backed by a single-pass
> tokenizer + stack (not recursive regex). Nesting is resolved by an explicit
> stack, so balancing itself does not introduce ReDoS; cost scales with
> tokenizer matches and the user-supplied open/close/ignore patterns.
> h3. API
> * {{BalancedGroup.find(text, openRegex, closeRegex[, options])}} — core
> entry point
> * {{CharSequence.findBalancedGroups(openRegex, closeRegex[, options])}} —
> GDK sugar (delegates to the above)
> * {{BalancedGroup.MatchOptions}} — immutable options ({{{}ignoreRegex{}}},
> {{{}includeEdges{}}}) with wither methods
> * Result tree: {{{}matchedString{}}},
> {{{}start{}}}/{{{}end{}}}/{{{}length{}}}, {{{}fullStart{}}}/{{{}fullEnd{}}},
> {{{}children{}}}, {{{}parent{}}}, {{depth}}
> h3. Behaviour notes
> * *Open/close* are user regexes for delimiters (must be non-empty).
> * {*}{{includeEdges}}{*}: when {{{}false{}}}, {{matchedString}} is the
> interior only (interval between open and close), while
> {{{}fullStart{}}}/{{{}fullEnd{}}} still cover the full pair including
> delimiters.
> * {*}{{ignoreRegex}}{*}: spans to skip while scanning (e.g. string literals,
> comments, self-closing tags).
> * Unmatched closers are ignored. Unclosed openers are dropped at
> end-of-input; completed groups nested inside them are promoted (“orphan
> rescue”).
> * Named groups {{{}OPEN{}}}, {{{}CLOSE{}}}, and {{IGNORE}} are reserved by
> the internal tokenizer.
> * Compiled tokenizers are cached (bounded LRU) for repeated
> open/close/ignore triples.
> h2. Examples
> {code:groovy}
> def roots = 'Root: (A + (B * C) + (D(E)))'.findBalancedGroups(/\(/, /\)/)
> assert roots.size() == 1
> assert roots[0].matchedString == '(A + (B * C) + (D(E)))'
> assert roots[0].children*.matchedString == ['(B * C)', '(D(E))']
> def opts = BalancedGroup.MatchOptions.defaults().withIncludeEdges(false)
> assert 'func(arg1, arg2)'.findBalancedGroups(/\(/, /\)/,
> opts)[0].matchedString == 'arg1, arg2'
> def code = 'if (true) { String s = "}"; }'
> def ignoreStrings =
> BalancedGroup.MatchOptions.defaults().withIgnoreRegex(/"(?:\\.|[^"\\])*"/)
> assert code.findBalancedGroups(/\{/, /\}/, ignoreStrings)[0].matchedString ==
> '{ String s = "}"; }'
> {code}
--
This message was sent by Atlassian Jira
(v8.20.10#820010)