Skip to content

Fix Java Regex Back References - #1834

Open
lmasroca wants to merge 4 commits into
masterfrom
fix-java-regex-backreferences
Open

lmasroca wants to merge 4 commits into
masterfrom
fix-java-regex-backreferences

Conversation

@lmasroca

@lmasroca lmasroca commented Oct 6, 2026

Copy link
Copy Markdown
Collaborator

Fixes for Java regex back references and one NPE fix for RegexHandler error listener.

RegexHandler error listener
ANTLR can call syntaxError with null RecognitionException, the parameter for this method was declared as non-nullable so we got NPE. Made that parameter nullable (RecognitionException?) so that we get ParseCancellationException instead of NullPointerException.

Non-capturing group increased capture group count (Java visitor)
visitAtom added a placeholder to captureGroups for every group but only filled it for capturing ones. This shifts the group counts after a non-capturing group. For example, on (?:abc)(def)\1 \1 should point to the only capture group (def) but because of this issue (?:abc) (a non-capturing group) was placing a placeholder on captureGroups' first position which would not get replaced as it is not a capture group, (def) would get placed as a second group which is incorrect. The placeholders are now added only when the group is a capturing group.

Fix back references
Currently back references worked by saving a reference to the captured group (val captureGroup: DisjunctionListRxGene? on BackReferenceRxGene's constructor). The problem with this is that when we use copy() on BackReferenceRxGene we would get the same reference. This is an issue when copying a regex gene tree, because we would get the back references on the copied tree pointing to the groups from the first tree. This is also a problem for quantified groups, as the groups are part of the quantifier template and we add atoms to quantifier genes by using template.copy(), and the reference would still point to the group within the template.

To fix this:

  • Capturing group's DisjunctionListRxGene saves its group number (null if non-capturing).
  • When a capture group is rendered it records its value in the RegexGene on the regex gene tree root, this is cleared when rendering the tree from the root.
  • The BackReferenceRxGenes read their group's last captured value from RegexGene. This gene now saves a boolean (instead of the capture group's gene) indicating whether the backref is unsatisfiable or not, this is used to prune unsatisfiable branches. In Java backrefs are unsatisfiable when forward or self reference, unsatisfiable or missing group.
  • Back references with no recorded value get "", this is the JS behavior but for JVM regex the Pattern check on RegexGene would make mismatches re-sample.
  • AssertionRxGene also render their innerGene when they contain a capture group (for example (?=(a)*)), so that those groups get recorded.

@lmasroca
lmasroca requested a review from jgaleotti October 6, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants