Написание парсера запросов PHP с регулярными выражениями

Я пытаюсь написать правильное регулярное выражение в PHP для анализа строки (написанной некоторым пользователем) для создания запроса. Он может быть таким же сложным, как:

name = 'benjo' and (surname = 'benny' or surname = 'bennie') or age = 4 

Позже я проанализирую строку для создания mySQL-запросов. На данный момент я просто пытаюсь найти правильное регулярное выражение для синтаксического анализа этой строки в массив, который может выглядеть так:

 $result = array( 0 => name = 'benjo', 1 => and 2 => array( 0 => surname = 'benny', 1 => or, 2 => surname = 'bennie', ), 3 => age = 4 ); 

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

 "#\((([^()]+|(?R))*)\)|(ou[^()])|(et[^()])#", 

который, конечно, не работает.

Я буду рад, если кто-то сможет помочь, я застрял здесь! 🙂 Tks, Romain

ПОЗВОЛЯЙТЕ ПРОБЛЕМ! 🙂 Хорошо, теперь давайте сделаем это немного более простым. Что бы это взяло с регулярным выражением и добавлением ограничения, что мы остаемся на «первом уровне» !! Нет вложенных круглых скобок, только одного уровня, но все равно столько И / ИЛИ … Может ли это что-нибудь изменить или REGEXP? (Я действительно хотел бы избежать написания моего мини-парсера, хотя это звучит действительно интересно …

Теоретическое регулярное выражение недостаточно мощно для сопоставления скобок. Теоретическое регулярное выражение может принимать только правила левой рекурсии / правильной рекурсии. Правила средней рекурсии не могут быть выражены с помощью регулярного выражения (например, <exp> -> "(" <exp> ")" ).

Однако регулярное выражение в языках программирования реализует функции, позволяющие регулярному выражению превосходить возможности регулярной грамматики. Например, backreference в regex позволяет писать регулярное выражение, которое соответствует неконтекстно-свободным языкам . Тем не менее, даже с backreference, по-прежнему невозможно сопоставить круглые скобки с регулярным выражением.

Поскольку библиотека PCRE поддерживает рекурсивное регулярное выражение с помощью функции вызова подпрограммы , технически возможно проанализировать такое выражение с помощью регулярного выражения. Однако, если вы не можете самостоятельно написать регулярное выражение , что означает, что вы понимаете, что вы делаете, и можете изменять регулярное выражение в соответствии с вашими потребностями, вы должны просто написать собственный парсер . В противном случае у вас будет бесполезный беспорядок.

 (?(DEFINE) (?<string>'[^']++') (?<int>\b\d+\b) (?<sp>\s*) (?<key>\b\w+\b) (?<value>(?&string)|(?&int)) (?<exp>(?&key) (?&sp) = (?&sp) (?&value)) (?<logic>\b (?:and|or) \b) (?<main> (?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) ) (?: (?&sp) (?&logic) (?&sp) (?&token) )* ) ) (?: ^ (?&sp) (?= (?&main) (?&sp) $ ) | (?!^) \G (?&sp) (?&logic) (?&sp) ) (?: \( (?&sp) (?<m_main>(?&main)) (?&sp) \) | (?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value)) ) 

Демо на regex101

Регулярное выражение выше должно использоваться с preg_match_all и помещаться между разделителем с флагом x (режим свободного /.../x ): /.../x .

Для каждого матча:

  • Если m_main захвата m_main имеет контент, поместите контент через другой раунд сопоставления.
  • В противном случае получите ключ и значение в m_value захвата m_key и m_value .

объяснение

Блок (?(DEFINE)...) позволяет вам определять именованные группы захвата для использования в вызовах подпрограмм отдельно от основного шаблона.

 (?(DEFINE) (?<string>'[^']++') # String literal (?<int>\b\d+\b) # Integer (?<sp>\s*) # Whitespaces between tokens (?<key>\b\w+\b) # Field name (?<value>(?&string)|(?&int)) # Field value (?<exp>(?&key) (?&sp) = (?&sp) (?&value)) # Simple expression (?<logic>\b (?:and|or) \b) # Logical operators (?<main> # <token> ( <logic> <token> )* # A token can contain a simple expression, or open a parentheses (...) # When we open a parentheses, we recurse into the main pattern again (?<token> \( (?&sp) (?&main) (?&sp) \) | (?&exp) ) (?: (?&sp) (?&logic) (?&sp) (?&token) )* ) ) 

Остальная часть шаблона основана на этом методе, чтобы соответствовать всем <token> s в <token> ( <logic> <token> )* с глобальной операцией согласования.

Последняя часть регулярного выражения, в то время как может быть записана как (?&token) , расширяется, чтобы соответствовать имени и значению поля в простых выражениях.

 (?: \( (?&sp) (?<m_main>(?&main)) (?&sp) \) | (?<m_key>(?&key)) (?&sp) = (?&sp) (?<m_value>(?&value)) )