将Backus-Naur形式语法转换为.Net正则表达式

时间:2009-01-20 23:35:31

标签: .net regex bnf

有没有办法将以下Backus-Naur形式(BNF)语法转换为.Net正则表达式? (我没有被困在BNF上,但我认为这可能是解释我想要做的事情的最佳方式。)

<field> ::= "<<" <fieldname> <options> ">>"

<options> ::= "" | "(" <option> ")"

<option> ::= "" | 
             <option> <non-paren> | 
             <option> <escaped-character>

<escaped-character> ::= "\\" | "\)"

<non-paren> ::= any character but paren

<fieldname> ::= any string that doesn't contain "(" or ">>"

我很接近,但我无法弄清楚如何处理转义\)。这会捕获命名组中的fieldnameoption

<<(?<fieldname>.\*?)(\((?<option>.*?)\))?>>

修改

事实证明,我对BNF语法比我想象的更生气。

我试图得到的是括号是特殊字符。在“选项”部分中,它们必须通过斜杠进行转义。 (还必须转义斜杠)。

4 个答案:

答案 0 :(得分:8)

BNF用于描述正则表达式通常无法描述的无上下文语言。无上下文语言与正则表达式的区别在于无上下文语言可以同时在双方进行递归。一个典型的例子是平衡括号问题。

paren = paren paren
      | '(' paren ')'  <-- there are characters on both sides of the recursion
      | ''

在您的情况下,您不使用任何双面递归,因此它会缩减为常规语言。

fieldname = /(?:>?[^(>])+/    //No double >, but single ones are ok.
option = /(?:[^()\\]|\\.)*/   //No parens, unless preceeded by \

pattern = /<<(?<fieldname>   )(?:\((?<option>   )\))?>>/

把它放在一起:

pattern = /<<(?<fieldname>(?:>?[^(>])+)(?:\((?<option>(?:[^()\\]|\\.)*)\))?>>/

一些边境案件:

<<f>oo(bar>>)>> --> ('f>oo', 'bar>>')
<<foo(bar\))>>  --> ('foo', 'bar\)')
<<foo(bar\\)>>  --> ('foo', 'bar\\')
<<foo\(bar)>>   --> ('foo\', 'bar')

修改

如果您想要在<<>>内转义任何额外的括号字符(和反斜杠),您可以执行以下操作:

fieldname = /(?:<?[^()\\<]|<?\\[()\\])+/
options = /(?:[^()\\]|\\[()\\])*/
pattern = /<<(?<fieldname>   )(?:\((?<option>   )\))?>>/

/<<(?<fieldname>(?:<?[^()\\]|<?\\[()\\])+)(?:\((?<option>(?:[^()\\]|\\[()\\])*)\))?>>/

更新

<<f>oo(bar>>)>> --> ('f>oo', 'bar>>')
<<foo(bar\))>>  --> ('foo', 'bar\)')
<<foo(bar\\)>>  --> ('foo', 'bar\\')
<<foo\(bar)>>   --> doesn't match
<<foo\((bar)>>  --> ('foo\(', 'bar')

答案 1 :(得分:2)

正则表达式表示常规语言。无上下文语法生成无上下文语言。前一种语言集是后者的一个子集,在一般情况下,你不能将无上下文的语言表达为正则表达式。

答案 2 :(得分:0)

我一直在思考一个答案,并希望有人会跳我,所以我可以停下来。 :)

BNF的递归性质通常是一个很好的开场指标,如果你的问题很好地映射到BNF,它就不能很好地映射到RegExp。

我不得不承认,我不确定我是否能算出你的BNF。例如: x :: =&lt;&lt; Boo(abc321)&gt;&gt;

建议你的'选项'对是c3,b2和a1。这假定char是一个有效的“选项” - 您没有为不是空字符串的选项定义任何有效的终端值。这真的是意图吗?

假设你不想被递归... 处理逃避和其他一切...... 你可能只是更好地编写代码。这看起来比通过其他任何东西更容易遍历字符串和处理。您想要的感觉表明您不需要任何前瞻或回顾逻辑。

答案 3 :(得分:0)

我想我设法让它发挥作用......

<<(?<fieldname>[^\(]+)(?<options>\((?<option>(\\\\|\\\)|[^\\\)])*)\))?>>

我能想到的诀窍是选项部分:

option =    (\\\\|\\\)||[^\\\)]

这意味着:无论是双斜线,斜线变换,还是非斜线变形。

然后将其包含0次或更多次并将其拍在名为“option”的组中:

((?<option>(\\\\|\\\)|[^\\\)])*)

我还将fieldname更改为一个或多个非开放式:

fieldname =     [^\(]+

把它放在一起,我提出了解决方案。