Changelog
View SourceAll notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[Unreleased]
[0.1.0] - 2026-07-31
Added
Xeger.random/2-- generate a single random string matching a pattern directly, without enumerating the full match set (much cheaper thanXeger.take(pattern, 1)for a pattern with a large or unbounded match count). Accepts:seedfor reproducible output and the existing:max_repeat/:alphabetoptions; for an unbounded quantifier with no:max_repeat, each repeat beyond the quantifier's minimum is a coin flip on whether to continue (unbounded in principle, geometrically short in practice) rather than a uniform draw, since there's no finite range to draw uniformly from without a cap. RaisesArgumentErrorfor a pattern with no possible matches at all.- The
~Xsigil replaces~G:~X/pattern/now callsrandom/1and returns one random matching string;~X/pattern/cand~X/pattern/sstill compile or stream, same as~Gdid.
Changed
:max_repeatnow defaults to unbounded instead of5.*,+, and{m,}are genuinely infinite by default:Xeger.stream/2enumerates them lazily, one length at a time, forever, rather than silently stopping at 5 repeats.take/3is unaffected either way (it only ever pullsnelements), but code that calledstream/2on an unbounded pattern and then consumed it eagerly (Enum.to_list/1,Enum.count/1, ...) without passing:max_repeatwill now hang instead of returning -- passmax_repeat: 5explicitly to keep the old behavior.- As part of the above, a repeated unit that can itself match the empty
string (e.g.
(a?)*) no longer relies on:max_repeatto terminate: the generator now caps that case atlen + 1candidate repeat-counts per target length internally, regardless of whether:max_repeatis set. - The pattern parser (
Xeger.Parser) is now generated bymix ichor.genfrom a declarative grammar (priv/grammar/xeger.aether) via Ichor, instead of a hand-written recursive-descent parser. The generated module (lib/xeger/grammar.ex) is checked in like any other source file;Xeger.Parseris now a thin adapter translating its errors to a plain message.Xeger.Parser.ActionsbuildsXeger.ASTnodes directly from the grammar's parse tree. ichor(~> 0.2.1, the grammar compiler) is adev-only,runtime: falsedependency -- it never ships.ichor_runtime(~> 0.1.0, the small support library the generated parser calls at match time, published as its own independent Hex package) is the only new runtime dependency.
Fixed
Xeger.stream/2/take/3could take combinatorially long enumerating a sequence of several fixed- or narrow-range parts at a large target length (e.g.Xeger.take("a+", 1, max_repeat: 50)at length 25 -- up toC(49,24)candidate splits, nearly all invalid).distributions/3now prunes candidate splits by each part's ownmax_leninstead of trying every nonnegative composition of the remaining length -- this was latent even before unbounded quantifiers became infinite by default.