fix(dummies): align StringMatching dialect edges with the real engine - #273
Merged
Conversation
Four cosmetic StringMatching edges from the ADR-0025 dialect, all
failing in the safe direction (a refusal or a wrong message, never a
mis-generated value), share one spine: a pattern the real .NET engine
accepts must never surface as ArgumentException — the taxonomy the
parser advertises (ArgumentException == malformed == the engine
rejects it).
- The over-limit message blamed "a nested unbounded quantifier
expanding without bound" even for a purely bounded pattern such as
(a{1000}){1000}, which asks for 1,000,000 characters with no
unbounded quantifier at all. State the fact and offer both causes;
the ceiling is a resource guard, not a satisfiability conflict, so
the pattern still parses and fails at generation.
- Repeated and quantified boundary anchors are no-ops the engine
accepts and matches (^^, $$, ^*, $?, ^{2}), yet ^^abc was accepted
while abc$$ was refused. Consume a run of, or a quantifier on, a
boundary anchor as the no-op it is — closing the ^^/$$ asymmetry.
- A leading '-[' in a class is an ordinary hyphen to the engine
([-[x]] = {-,[,x} then ']'), not a subtraction, yet it was refused.
Treat '-[' as subtraction only when a base member precedes — which
also correctly refuses the member-preceding forms [a-[x]] / [ab-[b]]
the engine reads as subtraction (previously mis-reported as a
malformed out-of-order range).
- A negated class that excludes the whole printable-ASCII universe
([^\x20-\x7E], [^\s\S]) is well-formed and regular; refuse it as
UnsupportedRegexException (a universe limit) with a tailored message,
not as a malformed pattern.
An oracle-backed theory pins the spine: every listed .NET-accepted
pattern generates or is refused as unsupported, never as malformed.
Refs: #210
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zJD4hf5JiKGt3D5HducWT
Reefact
enabled auto-merge
July 21, 2026 18:46
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.
Summary
Corrects four cosmetic edges in the
Dummiesregex subsystem (StringMatching), all of which failed in the safe direction (a refusal or a wrong message, never a mis-generated value). They share one spine: a pattern the real .NET engine accepts must never surface asArgumentException, which the parser advertises as meaning "malformed / the engine rejects it".Type of change
Changes
RegexNode.cs): the ceiling exception blamed "a nested unbounded quantifier expanding without bound" even for a purely bounded pattern such as(a{1000}){1000}(1,000,000 characters, no unbounded quantifier at all). It now states the fact and offers both possible causes. The ceiling is a resource guard, not a satisfiability conflict, so the pattern still parses and fails at generation.RegexParser.cs):^^,$$,^*,$?,^{2}are all no-ops the engine accepts and matches, yet^^abcwas accepted whileabc$$was refused. A run of, or a quantifier on, a boundary anchor is now consumed as the no-op it is, closing the^^/$$asymmetry. A misplaced anchor (a^,$a) is still refused.-[in a class is a literal hyphen (RegexParser.cs):[-[x]]is{-,[,x}then a literal]to the engine, not a subtraction.-[is now treated as subtraction only when a base member precedes it — which also correctly refuses the member-preceding forms[a-[x]]/[ab-[b]]the engine reads as subtraction (previously mis-reported as a malformed out-of-order range).RegexParser.cs): a negated class that excludes every printable-ASCII character ([^\x20-\x7E],[^\s\S]) is well-formed and regular; it is now refused asUnsupportedRegexException(a universe limit) with a tailored message, instead ofArgumentException("malformed"). This changes the exception type for these patterns.AnyPatternTests.cs): a theory asserts that every listed .NET-accepted pattern generates or is refused as unsupported, never as malformed — guarding the whole rejection channel, not just these instances. The newly-accepted patterns are also added to the existing real-engine oracle theory.Testing
dotnet build FirstClassErrors.sln(0 warnings, 0 errors)dotnet test FirstClassErrors.sln(all projects pass;Dummies.UnitTests260/260)FirstClassErrors.Analyzers.UnitTests132/132)Documentation
doc/updateddoc/handwritten/for-users/README.fr.md) updated if user-facing behavior changedIn-code XML dialect docs on
RegexParser/RegexGenerationContextwere updated to match the new behavior. No user-facing dialect reference exists to sync (ADR-0025's "document the supported dialect" follow-up is still open) and the README does not document the regex dialect, so nodoc/or French-translation change applies.Architecture decisions
Checked against the ADR base: the change stays within the decided scope of ADR-0025 (generate from a home-grown regular subset; two-channel rejection taxonomy). The reinforced invariant — a .NET-accepted pattern is never reported as malformed — is a consequence of that taxonomy, not a new lasting decision, so no ADR is drafted or superseded.
Related issues
Closes #210
🤖 Generated with Claude Code
Generated by Claude Code