Repository navigation
fix(#41,#42,#74): atomic groups, possessive quantifiers, first-byte skip - #92
Merged
Merged
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add BitParallelGlushkovBytecodeGeneratorTest verifying that findFromWithSkip is emitted for .*a (single required last char) and findFrom for .*[abc] (multi-char class, no required char). Remove the startsAnywhere guard from the constructor so lastRequiredChar is always computed unconditionally. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…semantics) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ign) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
jbachorik
marked this pull request as ready for review
July 1, 2026 12:05
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7bae647e0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sessives - Parser: desugar possessives X*+/X++/X?+ to atomic groups; parse (?>...) - PikeVM: encode exit as -(entryPos+2) to preserve group identity post-exit - PikeVM: replace wrong inline pruning with post-step lookahead pruning: kill exit threads only when inside thread can consume the next char - Tests: update parser tests from throwsUnsupported to assertDoesNotThrow Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ytecodeGenerator Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6 of 12 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Implements three features from the open-issues design doc:
Parse-and-reject for atomic groups and possessive quantifiers (closes [feature] Atomic groups not supported ((?>...)) #41, [feature] Possessive quantifiers not supported (*+, ++, ?+, {n,m}+) #42):
(?>...)and possessive quantifiers (X*+,X++,X?+,X{n,m}+) previously silently returned wrong results. They now throwUnsupportedPatternExceptionimmediately at parse time.Native atomic group semantics (closes [feature] Atomic groups not supported ((?>...)) #41, [feature] Possessive quantifiers not supported (*+, ++, ?+, {n,m}+) #42): Full implementation of atomic groups via PikeVM thread pruning — AST field
GroupNode.atomic, NFAatomicEntry/atomicExitstate markers inThompsonBuilder, and sibling-thread kill logic inPikeVMMatcherwhen a thread crosses anatomicExitboundary. Possessive quantifiers are desugared to atomic groups at parse time. Patterns with atomic groups are routed toPIKEVM_CAPTUREvia a newPatternAnalyzergate.First-byte skip optimisation for
startsAnywherepatterns (closes perf: first-byte skip / literal-suffix acceleration for BITPARALLEL_GLUSHKOV find() #74):GlushkovAutomaton.findLastRequiredChar()identifies a single ASCII character that must appear at the last position of every match.BitParallelGlushkovRuntime.findFromWithSkip()usesString.indexOfto jump past non-candidate regions, then scans frommax(start, reqPos - MAX_POSITIONS).Motivation
Issues #41 and #42 were correctness violations: Reggie was silently accepting
(?>...)and*+syntax and returning wrong results (false positives), violating the correctness guarantee. Issue #74 is a performance enhancement that avoids O(n) scanning over regions that cannot contain a match.Related Issue(s)
Fixes #41, Fixes #42, Implements #74
Change Type
Checklist
./gradlew build)Performance Impact
The
findFromWithSkipoptimisation applies only toBITPARALLEL_GLUSHKOVpatterns wherestartsAnywhere=trueand a single required last character exists. For such patterns,find()skips over regions guaranteed to contain no match endpoint, reducing average-case scan cost. No regression on patterns where the optimisation does not apply (falls back tofindFrom).Additional Notes
scratchAtomicPoswas simplified from a depth-indexed 2-D array to a single sharedint[]during validation — this is correct becauseaddThreadis called sequentially (single-threaded DFS), so the scratch array is never accessed concurrently.StructuralHashwas updated to mix inhasAtomicGroupsto prevent structural cache collisions between atomic and non-atomic variants of the same pattern.