blob: c1fe4cde63d1a034849d3af5410ef8d39658674e [file] [log] [blame]
/*
Generators - components that generate strings for a given regex pattern.
For the moment undocumented, and is subject to change.
*/
module std.regex.internal.generator;
/*
Useful utility for self-testing, an infinite range of string samples
that _have_ to match given compiled regex.
Caveats: supports only a simple subset of bytecode.
*/
@trusted private struct SampleGenerator(Char)
{
import std.array : appender, Appender;
import std.format.write : formattedWrite;
import std.random : Xorshift;
import std.regex.internal.ir : Regex, IR, IRL;
import std.utf : isValidDchar, byChar;
Regex!Char re;
Appender!(char[]) app;
uint limit, seed;
Xorshift gen;
//generator for pattern r, with soft maximum of threshold elements
//and a given random seed
this(ref Regex!Char r, uint threshold, uint randomSeed)
{
re = r;
limit = threshold;
seed = randomSeed;
app = appender!(Char[])();
compose();
}
uint rand(uint x)
{
uint r = gen.front % x;
gen.popFront();
return r;
}
void compose()
{
uint pc = 0, counter = 0, dataLenOld = uint.max;
for (;;)
{
switch (re.ir[pc].code)
{
case IR.Char:
formattedWrite(app,"%s", cast(dchar) re.ir[pc].data);
pc += IRL!(IR.Char);
break;
case IR.OrChar:
uint len = re.ir[pc].sequence;
formattedWrite(app, "%s", cast(dchar) re.ir[pc + rand(len)].data);
pc += len;
break;
case IR.CodepointSet:
case IR.Trie:
auto set = re.charsets[re.ir[pc].data];
auto x = rand(cast(uint) set.byInterval.length);
auto y = rand(set.byInterval[x].b - set.byInterval[x].a);
formattedWrite(app, "%s", cast(dchar)(set.byInterval[x].a+y));
pc += IRL!(IR.CodepointSet);
break;
case IR.Any:
uint x;
do
{
x = rand(0x11_000);
}while (x == '\r' || x == '\n' || !isValidDchar(x));
formattedWrite(app, "%s", cast(dchar) x);
pc += IRL!(IR.Any);
break;
case IR.GotoEndOr:
pc += IRL!(IR.GotoEndOr)+re.ir[pc].data;
assert(re.ir[pc].code == IR.OrEnd);
goto case;
case IR.OrEnd:
pc += IRL!(IR.OrEnd);
break;
case IR.OrStart:
pc += IRL!(IR.OrStart);
goto case;
case IR.Option:
uint next = pc + re.ir[pc].data + IRL!(IR.Option);
uint nOpt = 0;
//queue next Option
while (re.ir[next].code == IR.Option)
{
nOpt++;
next += re.ir[next].data + IRL!(IR.Option);
}
nOpt++;
nOpt = rand(nOpt);
for (;nOpt; nOpt--)
{
pc += re.ir[pc].data + IRL!(IR.Option);
}
assert(re.ir[pc].code == IR.Option);
pc += IRL!(IR.Option);
break;
case IR.RepeatStart:case IR.RepeatQStart:
pc += IRL!(IR.RepeatStart)+re.ir[pc].data;
goto case IR.RepeatEnd;
case IR.RepeatEnd:
case IR.RepeatQEnd:
uint len = re.ir[pc].data;
uint step = re.ir[pc+2].raw;
uint min = re.ir[pc+3].raw;
if (counter < min)
{
counter += step;
pc -= len;
break;
}
uint max = re.ir[pc+4].raw;
if (counter < max)
{
if (app.data.length < limit && rand(3) > 0)
{
pc -= len;
counter += step;
}
else
{
counter = counter%step;
pc += IRL!(IR.RepeatEnd);
}
}
else
{
counter = counter%step;
pc += IRL!(IR.RepeatEnd);
}
break;
case IR.InfiniteStart, IR.InfiniteBloomStart, IR.InfiniteQStart:
pc += re.ir[pc].data + IRL!(IR.InfiniteStart);
goto case IR.InfiniteEnd; //both Q and non-Q
case IR.InfiniteEnd, IR.InfiniteBloomEnd, IR.InfiniteQEnd:
uint len = re.ir[pc].data;
if (app.data.length == dataLenOld)
{
pc += IRL!(IR.InfiniteEnd);
break;
}
dataLenOld = cast(uint) app.data.length;
if (app.data.length < limit && rand(3) > 0)
pc = pc - len;
else
pc = pc + re.ir[pc].length;
break;
case IR.GroupStart, IR.GroupEnd:
pc += IRL!(IR.GroupStart);
break;
case IR.Bol, IR.Wordboundary, IR.Notwordboundary:
case IR.LookaheadStart, IR.NeglookaheadStart, IR.LookbehindStart, IR.NeglookbehindStart:
default:
return;
}
}
}
@property Char[] front()
{
return app.data;
}
enum empty = false;
void popFront()
{
app.shrinkTo(0);
compose();
}
}
@system unittest
{
import std.range, std.regex;
auto re = regex(`P[a-z]{3,}q`);
auto gen = SampleGenerator!char(re, 20, 3141592);
static assert(isInputRange!(typeof(gen)));
//@@@BUG@@@ somehow gen.take(1_000) doesn't work
foreach (v; take(gen, 1_000))
assert(v.match(re));
}