Skip to content

Dummies: minor StringMatching dialect edges (limit message, conservative refusals, negated-class taxonomy) #210

Description

@Reefact

Problem

Three small edges in the regex subsystem, all cosmetic and all failing in the safe direction (a refusal or a wrong message, never a mis-generated value):

  1. Misleading generation-limit message. RegexNode.cs:31-37 throws with a fixed message blaming "a nested unbounded quantifier … expanding without bound." But Any.StringMatching(@"(a{1000}){1000}").Generate() hits the 65 536-character ceiling (AnyPattern.cs:36) with no unbounded quantifier at all — the pattern deterministically asks for 1,000,000 characters. In a library whose signature is precise, honest diagnostics, a confidently wrong cause is off-brand.
  2. Conservative refusals of valid patterns, handled asymmetrically. ^* is accepted by .NET but Dummies throws ArgumentException (^ consumed as a no-op, leaving * with nothing to repeat). abc$$ is accepted by .NET but Dummies throws UnsupportedRegexException, while the symmetric ^^abc is accepted — an avoidable inconsistency.
  3. Negated-class taxonomy. A well-formed negated class whose members lie outside the printable universe is reported as ArgumentException ("malformed") instead of UnsupportedRegexException ("unsupported"). The ArgumentException-vs-UnsupportedRegexException boundary is advertised (RegexParser.cs:22-24) as mirroring the real engine; these edges overstate "malformed".

Impact

None mis-generates — every discrepancy is a refusal or a wrong message, so this is quality polish, not a contract break (contrast the balancing-group issue). But the misleading limit message can send a user hunting for a quantifier that does not exist, and the ^^/$$ asymmetry is a small inconsistency in a surface that otherwise reads as carefully symmetric.

Direction

  1. Reword the limit exception to state the fact and offer both causes, e.g. "The pattern produced a string longer than the 65536-character generation limit — a nested unbounded quantifier, or bounded quantifiers whose product exceeds the limit." Optionally compute the tree's minimum length at parse time and reject deterministically-over-limit patterns eagerly.
  2. Treat a $ whose successors are only more $ or end-of-branch as a no-op, mirroring the existing ^ handling; optionally accept a quantified leading anchor as no-op-with-quantifier.
  3. Only treat -[ as class subtraction when a member precedes it; classify well-formed-but-non-generatable negated classes as UnsupportedRegexException.

Any subset is acceptable — these are independent, low-priority polish items. Documenting them as known conservative refusals is a valid alternative for (2)/(3).

Acceptance criteria

  • The over-limit message no longer asserts an unbounded quantifier when the pattern has none; a test covers (a{1000}){1000}.
  • ^^abc and abc$$ are handled consistently (both accepted as no-op anchors, or both refused with the same channel), covered by tests.
  • Any change keeps every currently-accepted pattern generating a string the real .NET engine matches (the oracle test stays green).

Context

Surfaced by the 2026-07-20 architecture & design audit of the standalone Dummies library (doc/handwritten/for-maintainers/audit/2026-07-20-dummies-architecture-and-design-audit.md, §4.1(e); branch claude/dummies-architecture-audit-cru5aq).


Filed from the Claude Code session that produced the 2026-07-20 Dummies architecture & design audit: https://claude.ai/code/session_01GvrWFVoG74xiys3oyEft1G

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions