Regex

halotukozak.regex.Regex
See theRegex companion object
enum Regex

A symbolic regex algebra with exact language containment ("is this pattern a subset of that one?") in addition to the usual construction combinators — not just string matching. Pure Scala, no java.util.regex (or any other JVM-only API) anywhere in the implementation, so it compiles and runs identically on the JVM, Scala.js, and Scala Native.

Construct via smart factories on the companion (Regex.lit, Regex.literal, Regex.range, Regex.apply, Regex.alt, Regex.inter, Regex.all) and member combinators (a concat b, a | b, a & b, !a, r.star, r.repeat(n, m)). Raw case class constructors are inaccessible — only pattern matching via the generated unapply methods is allowed.

Supports literals, escapes (\d \D \s \S \w \W \t \n \r \f \a \e \v \cX \0[n[n]] \xhh \x{h...h} \uhhhh \Q...\E \R and meta-escapes), Unicode property escapes \p{...} \P{...} (general categories and ASCII-only POSIX classes; see UnicodeCategories), character classes (including ranges, negation, nested subclasses, and && intersection), ., alternation |, capturing groups (...), named groups (?...), non-capturing groups (?:...), lookahead (?=...) (?!...), the i inline flag (?i) (?-i) (?i:...) (?-i:...), anchors ^ $ \A \Z \z, quantifiers * + ? {n} {n,} {n,m} (bounds capped at Regex.maxRepeatBound).

Capturing groups are recognized structurally (numbered/named correctly, and fully transparent to every containment/matching operation - see Regex.Group and Subset's doc comment) but their captured spans aren't extractable yet - that's tracked separately.

Unsupported (parser returns RegexParseError.UnsupportedFeature): word-boundary anchors \b \B, \G, lookbehind (? \g{...}, Unicode script/block properties (\p{IsGreek}, \p{InGreek}), grapheme clusters \X.

For subset/emptiness/nullability queries and Brzozowski derivatives, see Subset.

Attributes

Companion
object
Source
Regex.scala
Graph
Supertypes
trait Enum
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all

Members list

Type members

Enum entries

final case class Alt

Alternation. Always size ≥ 2. parts is an AltBranches, not a plain Set like Inter: it still needs Set-like equality/hashing for ACI normalization (two Alts built from the same branches must be == and hash identically regardless of order, or Subset.isEmpty's visited-state dedup - and the finite-derivative-state-space guarantee it relies on - would silently stop collapsing equivalent states) - see AltBranches's own equals/hashCode. But iteration order matters too, unlike Inter: it's a pattern's only record of alternation branch priority (a|b prefers a), which every no-backtracking submatch algorithm needs for leftmost-first disambiguation (see Capture.scala) and which nothing before that ever needed to preserve. Regex.alt/| build it in exactly the order they're given (deduplicated, first occurrence kept) rather than normalizing it away.

Alternation. Always size ≥ 2. parts is an AltBranches, not a plain Set like Inter: it still needs Set-like equality/hashing for ACI normalization (two Alts built from the same branches must be == and hash identically regardless of order, or Subset.isEmpty's visited-state dedup - and the finite-derivative-state-space guarantee it relies on - would silently stop collapsing equivalent states) - see AltBranches's own equals/hashCode. But iteration order matters too, unlike Inter: it's a pattern's only record of alternation branch priority (a|b prefers a), which every no-backtracking submatch algorithm needs for leftmost-first disambiguation (see Capture.scala) and which nothing before that ever needed to preserve. Regex.alt/| build it in exactly the order they're given (deduplicated, first occurrence kept) rather than normalizing it away.

(A Scala 3 enum case can't carry its own equals/hashCode override directly - only plain classes can - hence AltBranches being a separate wrapper type instead of Alt's stored field simply being an order-preserving Vector[Regex] with the override inline.)

Attributes

Source
Regex.scala
final case class Chars

Matches any single code point in set.

Matches any single code point in set.

Attributes

Source
Regex.scala
final case class Compl

Complement ¬r.

Complement ¬r.

Attributes

Source
Regex.scala
final case class Concat

Concatenation a · b.

Concatenation a · b.

Attributes

Source
Regex.scala
case Empty extends Regex

Matches no string.

Matches no string.

Attributes

Source
Regex.scala
case Eps extends Regex

Matches the empty string ε.

Matches the empty string ε.

Attributes

Source
Regex.scala
final case class Group

Capturing group (...) / named group (?...). Purely a marker for where a capture begins and ends - L(Group(_, _, inner)) = L(inner) exactly, and every operation in this file (nullable, alphabetBoundaries, hasStartAnchor, and every smart constructor) treats it as fully transparent, recursing straight through to inner. Subset goes one step further and erases every Group node outright before ever deriving through it (see that file's doc comment for why) - so this node only matters to code that walks a Regex tree directly looking for where groups are, not to anything answering a containment/matching question through Subset.

Capturing group (...) / named group (?...). Purely a marker for where a capture begins and ends - L(Group(_, _, inner)) = L(inner) exactly, and every operation in this file (nullable, alphabetBoundaries, hasStartAnchor, and every smart constructor) treats it as fully transparent, recursing straight through to inner. Subset goes one step further and erases every Group node outright before ever deriving through it (see that file's doc comment for why) - so this node only matters to code that walks a Regex tree directly looking for where groups are, not to anything answering a containment/matching question through Subset.

index is the group's 1-based number, assigned left-to-right by opening paren (0 is reserved for "the whole match", which isn't its own node); name is Some for (?...), None for a bare (...). (?:...) (explicitly non-capturing) produces no Group node at all, same as today.

Attributes

Source
Regex.scala
final case class Inter

Intersection. Stored as a Set for ACI normalization. Always size ≥ 2.

Intersection. Stored as a Set for ACI normalization. Always size ≥ 2.

Attributes

Source
Regex.scala
final case class Look

Zero-width lookahead assertion. positive = true is (?=r): holds at a position iff some prefix of the string remaining there is in L(r), without consuming any of it. positive = false is (?!r): holds iff no such prefix exists.

Zero-width lookahead assertion. positive = true is (?=r): holds at a position iff some prefix of the string remaining there is in L(r), without consuming any of it. positive = false is (?!r): holds iff no such prefix exists.

Attributes

Source
Regex.scala
final case class Repeat

Bounded repetition r{lo,hi} (hi may be Int.MaxValue for the unbounded {lo,} form). Kept as a single node carrying lo/hi symbolically, instead of unfolding into that many copies of r - see Regex.repeat for why, and Subset.rawDerive for how a Brzozowski derivative is taken through it without ever materializing the unfolded form.

Bounded repetition r{lo,hi} (hi may be Int.MaxValue for the unbounded {lo,} form). Kept as a single node carrying lo/hi symbolically, instead of unfolding into that many copies of r - see Regex.repeat for why, and Subset.rawDerive for how a Brzozowski derivative is taken through it without ever materializing the unfolded form.

Attributes

Source
Regex.scala
final case class Star

Kleene star r*.

Kleene star r*.

Attributes

Source
Regex.scala
case StartAnchor extends Regex

^/\A (start-of-input anchor). Unlike Look, this can't be resolved by re-deriving a sub-pattern alongside whatever follows it - it's satisfied only in the state before any character has been consumed by anything, anywhere in the whole match, not just locally at this node. See Subset.derive's unconditional post-pass, which is what actually enforces that. $/\Z/\z don't need an equivalent node - under this library's whole-string matches() semantics they're all exactly (?!.) (negative lookahead of any code point), expressed directly with Look at parse time.

^/\A (start-of-input anchor). Unlike Look, this can't be resolved by re-deriving a sub-pattern alongside whatever follows it - it's satisfied only in the state before any character has been consumed by anything, anywhere in the whole match, not just locally at this node. See Subset.derive's unconditional post-pass, which is what actually enforces that. $/\Z/\z don't need an equivalent node - under this library's whole-string matches() semantics they're all exactly (?!.) (negative lookahead of any code point), expressed directly with Look at parse time.

Attributes

Source
Regex.scala

Value members

Concrete methods

infix def concat(b: Regex): Regex
Extension method from Regex

Attributes

Source
Regex.scala