Xeger (Xeger v0.1.0)

View Source

Xeger turns a regex-like pattern into a stream of strings that match it.

This library is intentionally focused on a generatable subset of regular expressions. In particular, it does not support lookarounds or backreferences.

Supported syntax (subset)

  • Literals: abc
  • Escapes: \(, \], \{, etc.
  • Dot: . (matches one codepoint from :alphabet)
  • Alternation: a|b|c
  • Grouping: (ab|c)
  • Character classes: [abc], [a-z], [^0-9]
  • Shorthands: \d (0-9), \w (A-Z a-z 0-9 _), \s (whitespace)
  • Repetition: *, +, ?, {m}, {m,}, {m,n}

Quantifiers that are unbounded (*, +, {m,}) are, by default, genuinely unbounded: stream/2 enumerates them lazily forever. Pass :max_repeat to cap them instead.

Ordering

stream/2 enumerates matches in shortlex order: shortest strings first, then (within the same length) a deterministic order.

Options

  • :max_repeat (default: none - unbounded) - caps *, +, {m,} at this many repeats; without it, stream/2 on such a pattern is an infinite stream (safe to pipe into Enum.take/2, unsafe to pipe into anything that consumes it eagerly, like Enum.to_list/1 or Enum.count/1)
  • :alphabet (default: printable ASCII) - set used for . and negated classes

Examples

iex> Xeger.take("a(b|c){2}\d", 6)
["abb0", "abb1", "abb2", "abb3", "abb4", "abb5"]

iex> Xeger.take("a*", 8)
["", "a", "aa", "aaa", "aaaa", "aaaaa", "aaaaaa", "aaaaaaa"]

iex> Xeger.take("a*", 6, max_repeat: 3)
["", "a", "aa", "aaa"]

Summary

Functions

Compile a pattern into a %Xeger.Pattern{}.

Compile a pattern into a %Xeger.Pattern{} or raise.

Sanity helper: test a string against Elixir's Regex.

Generate a single random string matching a pattern, without enumerating the full match set.

Custom sigil for Xeger patterns.

Create an (often infinite) stream of matches.

Convenience: take n matches from a pattern.

Types

option()

@type option() ::
  {:max_repeat, non_neg_integer()}
  | {:alphabet, [non_neg_integer()]}
  | {:seed, integer()}

Functions

compile(pattern, opts \\ [])

@spec compile(binary(), [option()]) :: {:ok, Xeger.Pattern.t()} | {:error, binary()}

Compile a pattern into a %Xeger.Pattern{}.

Returns {:ok, pattern} or {:error, message}.

compile!(pattern, opts \\ [])

@spec compile!(binary(), [option()]) :: Xeger.Pattern.t()

Compile a pattern into a %Xeger.Pattern{} or raise.

matches?(pattern, string)

@spec matches?(binary(), binary()) :: boolean()

Sanity helper: test a string against Elixir's Regex.

This is useful for tests and debugging (not used in generation).

random(pattern, opts \\ [])

@spec random(Xeger.Pattern.t() | binary(), [option()]) :: binary()

Generate a single random string matching a pattern, without enumerating the full match set.

Accepts either a binary pattern string or a compiled Pattern.t(). Pass :seed for reproducible output; without it, each call draws fresh randomness. :max_repeat and :alphabet behave as in stream/2 -- without :max_repeat, each repeat of an unbounded quantifier beyond its minimum is a coin flip on whether to continue rather than a uniform draw (see Xeger.Random for the exact distribution), so output length is unbounded in principle but usually short.

Raises ArgumentError if the pattern (combined with :alphabet) has no possible matches at all (e.g. a negated class that excludes the whole alphabet).

Examples

iex> Xeger.random("a{3}", seed: 1)
"aaa"

iex> Xeger.random("[0-9]{4}", seed: 1) |> String.match?(~r/^[0-9]{4}$/)
true

sigil_X(pattern, modifiers \\ [])

@spec sigil_X(
  binary(),
  charlist()
) :: binary() | Xeger.Pattern.t() | Enumerable.t()

Custom sigil for Xeger patterns.

The ~X sigil generates one random string matching the pattern by default -- see random/1. Use a modifier for the other forms:

Modifiers

Examples

iex> ~X/[0-9]{4}/ |> String.match?(~r/^[0-9]{4}$/)
true

iex> pattern = ~X/[0-9]{3}/c
iex> Xeger.take(pattern, 5)
["000", "001", "002", "003", "004"]

iex> ~X/a+/s |> Enum.take(3)
["a", "aa", "aaa"]

stream(pattern_or_compiled, opts \\ [])

@spec stream(Xeger.Pattern.t() | binary(), [option()]) :: Enumerable.t()

Create an (often infinite) stream of matches.

take(pattern, n, opts \\ [])

@spec take(Xeger.Pattern.t() | binary(), pos_integer(), [option()]) :: [binary()]

Convenience: take n matches from a pattern.

Accepts either a binary pattern string or a compiled Pattern.t().