Вернуть конечное множество, соответствующее выражению регулярного выражения - PullRequest
1 голос
/ 17 февраля 2011

Что-то похожее на http://regexio.com/prototype.html, Я пытаюсь получить набор, соответствующий определенному регулярному выражению.

Ответы [ 2 ]

3 голосов
/ 18 февраля 2011

По сути, вам нужно проанализировать регулярное выражение, а затем вместо чтения ввода при обходе проанализированного выражения вывода вариантов.

Я взломал следующую программу, которая делает то, что вам нужно для очень простого регулярного выражения (только альтернативные опции, использующие |, итерация с использованием *, группировка с использованием () и экранирование с использованием \ поддерживается). Обратите внимание, что итерация выполняется просто 0–5 раз, преобразование в возможно бесконечную итерацию оставлено в качестве упражнения для читателя; -).

Я использовал простой синтаксический анализатор с рекурсивным спуском, строящий абстрактное синтаксическое дерево в памяти; это дерево в конце концов пройдено, и все возможные наборы построены. Решение, вероятно, совсем не оптимально, но оно работает. Наслаждайтесь:

public class TestPrg
{
    static void Main()
    {
        var expression = new RegexParser("a(b|c)*d").Parse();

        foreach (var item in expression.Generate())
        {
            Console.WriteLine(item);
        }
    }
}

public static class EnumerableExtensions
{
    // Build a Cartesian product of a sequence of sequences
    // Code by Eric Lippert, copied from <http://blogs.msdn.com/b/ericlippert/archive/2010/06/28/computing-a-cartesian-product-with-linq.aspx>
    public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
    {
        IEnumerable<IEnumerable<T>> emptyProduct = new[] { Enumerable.Empty<T>() };
        return sequences.Aggregate(
            emptyProduct,
            (accumulator, sequence) =>
            from accseq in accumulator
            from item in sequence
            select accseq.Concat(new[] { item }));
    }
}

public class RegexParser
{
    private const char EOF = '\x0000';
    private readonly string str;
    private char curr;
    private int pos;

    public RegexParser(string s)
    {
        str = s;
    }

    public RegExpression Parse()
    {
        pos = -1;
        Read();
        return ParseExpression();
    }

    private void Read()
    {
        ++pos;
        curr = pos < str.Length ? str[pos] : EOF;
    }

    private RegExpression ParseExpression()
    {
        var term = ParseTerm();
        if (curr == '|')
        {
            Read();
            var secondExpr = ParseExpression();
            return new Variants(term, secondExpr);
        }
        else
        {
            return term;
        }
    }

    private RegExpression ParseTerm()
    {
        var factor = ParseFactor();
        if (curr != '|' && curr != '+' && curr != '*' && curr != ')' && curr != EOF)
        {
            var secondTerm = ParseTerm();
            return new Concatenation(factor, secondTerm);
        }
        else
        {
            return factor;
        }
    }

    private RegExpression ParseFactor()
    {
        var element = ParseElement();
        if (curr == '*')
        {
            Read();
            return new Repeat(element);
        }
        else
        {
            return element;
        }
    }

    private RegExpression ParseElement()
    {
        switch (curr)
        {
            case '(':
                Read();
                var expr = ParseExpression();
                if (curr != ')') throw new FormatException("Closing paren expected");
                Read();
                return expr;

            case '\\':
                Read();
                var escapedChar = curr;
                Read();
                return new Literal(escapedChar);

            default:
                var literal = curr;
                Read();
                return new Literal(literal);
        }
    }
}

public abstract class RegExpression
{
    protected static IEnumerable<RegExpression> Merge<T>(RegExpression head, RegExpression tail, Func<T, IEnumerable<RegExpression>> selector)
        where T : RegExpression
    {
        var other = tail as T;
        if (other != null)
        {
            return new[] { head }.Concat(selector(other));
        }
        else
        {
            return new[] { head, tail };
        }
    }

    public abstract IEnumerable<string> Generate();
}

public class Variants : RegExpression
{
    public IEnumerable<RegExpression> Subexpressions { get; private set; }

    public Variants(RegExpression term, RegExpression rest)
    {
        Subexpressions = Merge<Variants>(term, rest, c => c.Subexpressions);
    }

    public override IEnumerable<string> Generate()
    {
        return Subexpressions.SelectMany(sub => sub.Generate());
    }
}

public class Concatenation : RegExpression
{
    public IEnumerable<RegExpression> Subexpressions { get; private set; }

    public Concatenation(RegExpression factor, RegExpression rest)
    {
        Subexpressions = Merge<Concatenation>(factor, rest, c => c.Subexpressions);
    }

    public override IEnumerable<string> Generate()
    {
        foreach (var variant in Subexpressions.Select(sub => sub.Generate()).CartesianProduct())
        {
            var builder = new StringBuilder();
            foreach (var item in variant) builder.Append(item);
            yield return builder.ToString();
        }
    }
}

public class Repeat : RegExpression
{
    public RegExpression Expr { get; private set; }

    public Repeat(RegExpression expr)
    {
        Expr = expr;
    }

    public override IEnumerable<string> Generate()
    {
        foreach (var subexpr in Expr.Generate())
        {
            for (int cnt = 0; cnt < 5; ++cnt)
            {
                var builder = new StringBuilder(subexpr.Length * cnt);
                for (int i = 0; i < cnt; ++i) builder.Append(subexpr);
                yield return builder.ToString();
            }
        }
    }
}

public class Literal : RegExpression
{
    public char Ch { get; private set; }

    public Literal(char c)
    {
        Ch = c;
    }

    public override IEnumerable<string> Generate()
    {
        yield return new string(Ch, 1);
    }
}
0 голосов
/ 17 февраля 2011

Мой ответ на похожий вопрос может сработать для вас. Если регулярное выражение не включает любые * операции (так что язык, который он распознает, конечно), должно быть легко переписать регулярное выражение в виде грамматики BNF, а затем выполнить анализ снизу вверх, производя конечные множества, соответствующие каждому нетерминальному символу, пока вы не достигнете начальный символ, после чего вы закончите.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...