У меня есть определенные логические предложения, и я хочу проверить их правильность с помощью Regex в C #.

Каждая заглавная буква является предикатом. Формулы для логики предикатов построены с предикатами и связями, такими как ¬, ⇒, ⇔, ⋀ и ⋁. Однако пользовательский ввод должен быть в строковой нотации ASCII, а именно:

Logical notation       ASCII               
¬A                     ~(A)                 Negation

A ⇒ B                 >(A,B)               Implication

A ⇔ B                 =(A,B)               Bi-Implication

A ⋀ B                  &(A,B)               AND

A ⋁ B                  |(A,B)               OR

Кроме того, True и False представлены 0 и 1, например, так: & (0,1)

Допустим, у меня есть следующий вход ASCII

string input1 = "&(&(=(A,B),>(&(A,B),~(C))),>(A,~(&(A,B))))"; // valid
string input2 = "1"     // valid
string input3 = "=(~(A),>(>(B,C),~(A)))" // valid
string input4 = "(~(A))" // invalid because no connective in the beginning
string input5 = ">(A,B"  // invalid because no closing parenthesis

Таким образом, строка ascii должна быть

  1. Единственный Предикат - A-Z или 0-1
  2. строка, начинающаяся с соединительного и содержащая два предложения, разделенных запятой, эти предложения могут быть либо одним предикатом, либо связующим с двумя предложениями ...

Я придумал это:

Regex checkExpression = new Regex(
                   @"([&|>=]\(([A-Z0-1]{1}|\(.*\)),([A-Z0-1]{1}|\(.*\))\))
                                          |([~]\(([A-Z0-1]{1}|\(.*\))\))");

Тем не менее, я не очень знаком со сборкой регулярных выражений, любая помощь приветствуется.

3
Lyubomir Dimov 4 Сен 2017 в 01:47

5 ответов

Лучший ответ

Я создал регулярное выражение, которое может сделать это. Регулярное выражение: ^([01A-Z](?![01A-Z])|(?<dyadic>[|&>=]\()|(?<comma-dyadic>\,)|(?<dBracket-comma>\))|(?<unary>~\()|(?<uBracket-unary>\)))+(?(dyadic)(?!))(?(comma)(?!))(?(unary)(?!))$

Это намного чище и лучше в PCRE, потому что вы можете делать рекурсию ^([A-Z01])|([>=&|])\((?R),(?R)\)|~\((?R)\)$, но это недоступно в C # в стиле regex.

Мне пришлось изучить балансировочную группу из C #, поэтому вам, возможно, придется изучить это.

Разбивка того, как работает код:

^                             # Start of line
(                             # Either
    [01A-Z](?![01A-Z])|       # A symbol or bool followed by anything else
    (?<dyadic>[|&>=]\((?!,))| # Start of dyadic
    (?<comma-dyadic>,(?!\)))| # Looks for comma followed by dyadic. Pops off the dyadic stack.
    (?<dBracket-comma>\))|    # Looks for ending bracket following comma. pops off comma stack. 
    (?<monadic>~\((?!\)))|    # Start of monadic function.
    (?<uBracket-monadic>\)))  # Looks for ending bracket for unary. Pops off the monadic stack. 
+                             # Any number of times.
(?(dyadic)(?!))               # Assert dyadic stack is empty. All have a comma.
(?(comma)(?!))                # Assert comma stack is empty. All dyadic commas followed by brackets.
(?(monadic)(?!))              # Assert monadic stack is empty. All monadic expressions have closing brackets.
$                             # End of line.

Пример демо-версии.

Обновление: забыл убедиться, что в каждой функции есть параметр. Отрицательный прогноз был добавлен в 3 местах, чтобы исправить это.

Update2: Сделано, чтобы регулярное выражение совпадало только с однобуквенными литералами. Добавлен негативный взгляд, который проверяет, сопровождается ли буквой или цифрой буквой или цифрой.

2
Alex Collins 6 Сен 2017 в 19:24

Как уже было сказано, регулярное выражение не является хорошим инструментом для этого. Даже если бы вы могли это сделать (я скорее сомневаюсь, что в этом случае), часто вам нужно не только подтвердить это, но и оценить. И вы действительно не хотите делать это с помощью регулярных выражений. И хотя для подхода к регулярным выражениям это кошмар, для грамматического парсера это просто пирог. Если вам нужно что-то более легкое, для простых случаев вы даже можете написать все самостоятельно, анализаторы LL (1) имеют достаточно описательный код:

public class ParseException : Exception
{
    public ParseException(string message) : base(message) { }
}

public class Analyzer
{
    protected int position;
    protected string input;
    protected Dictionary<char, bool> predicates;

    public Analyzer(string input)
    {
        this.input = input;
    }

    public bool? Evaluate(Dictionary<char, bool> predicates = null)
    {
        position = 0;
        this.predicates = predicates;

        try
        {
            bool value = T();
            if (position == input.Length)
            {
                return value;
            }
        }
        catch (ParseException)
        {
        }

        return null;
    }

    protected char GetChar()
    {
        if (position >= input.Length)
        {
            throw new ParseException("Unexpected end of input");
        }
        return input[position++];
    }

    protected void MatchChar(char c)
    {
        if (GetChar() != c)
        {
            throw new ParseException("Invalid input");
        }
    }

    protected bool T()
    {
        char c = GetChar();
        if (c == '~')
        {
            MatchChar('(');
            bool val = T();
            MatchChar(')');
            return !val;
        }
        if (c == '>')
        {
            MatchChar('(');
            bool val1 = T();
            MatchChar(',');
            bool val2 = T();
            MatchChar(')');
            return val2 || !val1;
        }
        if (c == '=')
        {
            MatchChar('(');
            bool val1 = T();
            MatchChar(',');
            bool val2 = T();
            MatchChar(')');
            return val1 == val2;
        }
        if (c == '&')
        {
            MatchChar('(');
            bool val1 = T();
            MatchChar(',');
            bool val2 = T();
            MatchChar(')');
            return val1 && val2;
        }
        if (c == '|')
        {
            MatchChar('(');
            bool val1 = T();
            MatchChar(',');
            bool val2 = T();
            MatchChar(')');
            return val1 || val2;
        }
        if (c == '0')
        {
            return false;
        }
        if (c == '1')
        {
            return true;
        }
        if (c >= 'A' && c <= 'Z')
        {                   
            if (predicates == null) { return false; }
            if (predicates.TryGetValue(c, out bool val))
            {
                return val;
            }
            throw new ParseException("Predicate value not found");
        }

        throw new ParseException("Invalid input");
    }
}

Вы можете проверить действительность следующим образом:

bool ok1 = new Analyzer(input1).Evaluate().HasValue;

И оцените так:

var values1 = new Dictionary<char, bool>() { ['A'] = true, ['B'] = false, ['C'] = true };
bool result1 = new Analyzer(input1).Evaluate(values1).Value;
1
Antonín Lejsek 4 Сен 2017 в 01:48

Если я вас правильно понимаю, вы хотите проверить синтаксис данного предложения.

Это можно легко сделать путем зацикливания и сжатия каждой действительной формулы для одного предиката, скажем, 1. Повторение этого до тех пор, пока остаток не станет единственным 1, будет сигнализировать правильное предложение. Завершение с No Match сигнализирует о неверном предложении.

Иллюстрация:

&(&(=(A,B),>(&(A,B),~(C))),>(A,~(&(A,B))))           Proposition
&(&(1,>(1,1)),>(A,~(1)))                             First iteration
&(&(1,1),>(A,1))                                     Second iteration
&(1,1)                                               Third iteration
1                                                    Fourth iteration

=(~(A),>(>(B,C),~(A)))                               Proposition
=(1,>(1,1))                                          First iteration
=(1,1)                                               Second iteration
1                                                    Third iteration

(~(A))                                               Proposition
(1)                                                  First iteration
            No Match

>(A,B                                                Proposition
            No Match

(Создано с помощью regex101)

Ваше регулярное выражение работает, но я немного упростил его:

~\([A-Z0-1]\)|[&|>=]\([A-Z0-1],[A-Z0-1]\)

Вот живая демонстрация в ideone.

1
SamWhan 6 Сен 2017 в 14:20

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

Я предлагаю использовать Абстрактные деревья синтаксиса (AST). Мне нравится ANTLR. Хорошее вступление можно найти по адресу ANTLR с C # - с использованием абстрактного синтаксического дерева (AST)

2
Richard Schneider 3 Сен 2017 в 23:14

Как сказал Ричард, вы должны использовать AST для управления валидацией, и на самом деле вы также можете использовать это, чтобы начать создавать свой собственный язык на C #. Я делал это много раз в прошлом для различных проектов и использовал довольно приличный инструмент под названием "Irony.Net" , где вы непосредственно разрабатываете грамматику в коде.

Irony - это набор разработчика для реализации языков в .NET Платформа. В отличие от большинства существующих решений в стиле yacc / lex, Irony не использовать любой сканер или генерацию кода парсера из грамматики спецификации написаны на специализированном метаязыке. В иронии грамматика целевого языка кодируется непосредственно в C # с помощью оператора перегрузка для выражения грамматических конструкций. Сканер и парсер Иронии модули используют грамматику, закодированную как класс c # для управления синтаксическим анализом обработать. Irony.Net CodePlex

С этим придумали довольно основную грамматику, которая, кажется, обрабатывает ваши случаи ниже. Однако в ваших примерах есть странный случай (или требует дальнейшего объяснения)

  1. 1 действителен, но является ли 0 действительным?
  2. Как и выше, как насчет любого из предикатов A-Z (верхний регистр)?

Пример грамматики

[Language("Logical Proposition", "1.0", "")]
public class LogicalPropositionGrammar : Grammar
{
    public LogicalPropositionGrammar()
    {
        //syntax terminals
        var lpar = ToTerm("(");
        var rpar = ToTerm(")");
        var comma = ToTerm(",");
        var trueTerm = ToTerm("1") | "true";
        var falseTerm = ToTerm("0") | "false";

        //nonterms
        var predicate = new NonTerminal("Predicate");
        var connective = new NonTerminal("Connective");
        var pexp = new NonTerminal("PredExpression");
        var formula = new NonTerminal("Formula");
        var literal = new NonTerminal("Literal");
        var singleTerm = new NonTerminal("SingleTerm");
        var multiTerm = new NonTerminal("MultiTerm");

        //formulat non terms
        var negation = new NonTerminal("Negation");
        var implication = new NonTerminal("Implication");
        var biImplication = new NonTerminal("Bi-Implication");
        var andTerm = new NonTerminal("And");
        var orTerm = new NonTerminal("Or");

        literal.Rule = trueTerm | falseTerm;
        singleTerm.Rule = lpar + pexp + rpar; //single term is (pexp)
        multiTerm.Rule = lpar + pexp + comma + pexp + rpar; //mult term = (pexp, pexp)

        //formula rules
        negation.Rule = ToTerm("~") + singleTerm;
        implication.Rule = ToTerm(">") + multiTerm;
        biImplication.Rule = ToTerm("=") + multiTerm;
        andTerm.Rule = ToTerm("&") + multiTerm;
        orTerm.Rule = ToTerm("|") + multiTerm;

        //predicate terms
        predicate.Rule = ToTerm("A") | "B" | "C" | "D" | "E" | "F" | "G" |
                            "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" |
                            "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" |
                            "X" | "Y" | "Z" | literal;

        //predicate rule
        pexp.Rule = predicate | negation | implication | biImplication | andTerm | orTerm;

        //a base formulat
        formula.Rule = MakeStarRule(formula, pexp);

        RegisterOperators(10, "&", "~", ">", "=", "|");
        MarkPunctuation(",", "(", ")");
        MarkTransient(pexp, singleTerm);

        Root = formula;
    }
}

Example 1 parser

4
Nico 4 Сен 2017 в 03:30