我有一定的逻辑命题,我想用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字符串应该是
我想出了这个:
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
但是,我对构建正则表达式并不是很熟悉,感谢任何帮助。
答案 0 :(得分:3)
正如Richard所说,您应该使用AST来管理验证,实际上您也可以使用它来开始在C#上构建自己的语言。我过去已经多次为各种项目做过这样的事情,并使用了一个名为“Irony.Net”irony的相当不错的工具,您可以直接在代码中设计语法。
Irony是一个用于在.NET上实现语言的开发工具包 平台。与大多数现有的yacc / lex式解决方案不同,Irony没有 从语法中使用任何扫描程序或解析器代码生成 用专门的元语言编写的规范。在反讽中 目标语言语法直接在c#中使用运算符编码 重载以表达语法结构。反讽的扫描仪和解析器 模块使用编码为c#类的语法来控制解析 处理。 Irony.Net CodePlex
这已经提出了一个非常基本的语法,似乎可以处理下面的情况。但是,您的示例中存在一个奇怪的情况(或需要进一步说明)
1
有效但0
有效吗?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;
}
}
答案 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
你的正则表达式工作,但我已经稍微简化了一下:
~\([A-Z0-1]\)|[&|>=]\([A-Z0-1],[A-Z0-1]\)
答案 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:使正则表达式只匹配单字母文字。添加了一个负向预测,检查字母或数字后面是否有字母或数字。