布尔道具匹配

时间:2017-09-03 22:47:47

标签: c# regex logic

我有一定的逻辑命题,我想用C#中的Regex检查它们的有效性。

每个大写字母都是谓词。谓词逻辑的公式是使用谓词和连接词构建的,如¬,⇒,⇔,⋀和⋁。但是,用户输入应采用ASCII字符串表示法,即:

select ID from to_db, to_db2 WHERE to_db.ID ==  to_db2

此外,True和False用0和1表示,如下所示:&(0,1)

假设我有以下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

因此,ascii字符串应该是

  1. 单谓词 - A-Z或0-1
  2. 字符串以连字符开头并包含两个以逗号分隔的命题,这些命题可以是单个谓词,也可以是带有两个命题的连词......
  3. 我想出了这个:

    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
    

    但是,我对构建正则表达式并不是很熟悉,感谢任何帮助。

5 个答案:

答案 0 :(得分:3)

正如Richard所说,您应该使用AST来管理验证,实际上您也可以使用它来开始在C#上构建自己的语言。我过去已经多次为各种项目做过这样的事情,并使用了一个名为“Irony.Net”的相当不错的工具,您可以直接在代码中设计语法。

  

Irony是一个用于在.NET上实现语言的开发工具包   平台。与大多数现有的yacc / lex式解决方案不同,Irony没有   从语法中使用任何扫描程序或解析器代码生成   用专门的元语言编写的规范。在反讽中   目标语言语法直接在c#中使用运算符编码   重载以表达语法结构。反讽的扫描仪和解析器   模块使用编码为c#类的语法来控制解析   处理。   Irony.Net CodePlex

这已经提出了一个非常基本的语法,似乎可以处理下面的情况。但是,您的示例中存在一个奇怪的情况(或需要进一步说明)

  1. 1有效但0有效吗?
  2. 如上所述任何谓词A-Z(大写)?
  3. 示例语法

    [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

答案 1 :(得分:2)

使用正则表达式进行语言分析是可能的,但速度非常快。

我建议使用抽象语法树(AST)。我喜欢ANTLR。可以在ANTLR with C# – using an Abstract Syntax Tree (AST)

找到一个好的介绍

答案 2 :(得分:1)

正如前面所说,正则表达式并不是一个很好的工具。即使你可以做到(我宁愿怀疑在这种情况下),通常你不仅需要验证它,还要评估。你真的不想用正则表达式那样做。而对于正则表达式而言,这是一场噩梦,对于gramatics解析器来说,这是一块蛋糕。如果您需要轻量级的东西,对于简单的情况,您甚至可以自己编写,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;

答案 3 :(得分:1)

如果我理解正确,您需要验证给定命题的语法。

这可以通过循环和每个有效公式收缩到单个谓词,比如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

Generated with regex101

你的正则表达式工作,但我已经稍微简化了一下:

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

Here's a live demo at ideone

答案 4 :(得分:1)

我创建了一个可以执行此操作的正则表达式。正则表达式:^([01A-Z](?![01A-Z])|(?<dyadic>[|&>=]\()|(?<comma-dyadic>\,)|(?<dBracket-comma>\))|(?<unary>~\()|(?<uBracket-unary>\)))+(?(dyadic)(?!))(?(comma)(?!))(?(unary)(?!))$

它在PCRE中更干净,更好,因为你可以进行递归^([A-Z01])|([>=&|])\((?R),(?R)\)|~\((?R)\)$,但这在C#的正则表达式中是不可用的。

我必须从C#学习balancing group,所以你可能需要研究一下。

代码如何工作的细分:

^                             # 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.

示例demo

更新:忘记确保每个功能都有一个参数。在3个位置添加了负向前瞻以解决此问题。

Update2:使正则表达式只匹配单字母文字。添加了一个负向预测,检查字母或数字后面是否有字母或数字。