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')
EDIT:
Если вы хотите, чтобы любые дополнительные символы в скобках (и обратные косые черты) должны были быть экранированы внутри <<
и >>
, вы можете сделать что-то вроде этого:
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')