Question

Quelle classe de langues modernes ne réelles regexes reconnaissent en réalité?

Chaque fois qu'il ya un groupe de capture d'une longueur illimitée avec une référence arrière (par exemple (.*)_\1) une expression régulière est correspondant maintenant une langue non régulière. Mais cela, à lui seul, ne suffit pas de faire correspondre quelque chose comme S ::= '(' S ')' | ε -. La langue sans contexte de paires de parens

récursives regexes (qui sont nouveau pour moi, mais je suis assuré exist en Perl et PCRE) semblent reconnaître au moins la plupart des lampes fluorescentes compactes.

Quelqu'un at-il fait ou lire des recherches dans ce domaine? Quelles sont les limites de ces expressions rationnelles « modernes »? Est-ce qu'ils reconnaissent strictement plus ou moins strictement CFG, de LL ou LR grammaires? Ou bien est-il existe deux langues qui peuvent être reconnues par une expression régulière mais pas un CFG et le contraire?

Liens vers les documents pertinents serait très apprécié.

Était-ce utile?

La solution

Motif Recursion

Avec des motifs récursifs, vous avez une forme de descente récursive correspondant .

Ceci est très bien pour une variété de problèmes, mais une fois que vous voulez réellement faire la descente récursive parsing , vous devez insérer des groupes de capture ici et là, et il est difficile à récupérer la structure complète d'analyse syntaxique de cette façon. Damian Conway de Regexp :: Grammars module Perl transforme le modèle simple dans un poste équivalent qui fait automatiquement tout ce qui a nommé la capture dans une structure de données récursive, ce qui rend pour une récupération beaucoup plus facile de la structure analysée. J'ai un échantillon comparant ces deux approches à la fin de cette publication.

Restrictions sur Recursion

La question est quel genre de grammaires récursives modèles peuvent correspondre. Eh bien, ils sont certainement descente récursive matchers de type. La seule chose qui vient à l'esprit est que motifs récursifs ne peuvent pas gérer gauche récursion cela met une contrainte que vous pouvez les appliquer sur les types de grammaires. Parfois, vous pouvez réorganiser vos productions pour éliminer la récursivité gauche.

BTW, PCRE et Perl diffèrent légèrement sur la façon dont vous êtes autorisé à formuler la récursivité. Voir les sections « RECURSIVE PATRONS » et « différence de récursivité de Perl » dans le pcrepattern manpage. par exemple:. Perl peut traiter ^(.|(.)(?1)\2)$ où PCRE nécessite la place ^((.)(?1)\2|.)$

Recursion Demos

Le besoin de motifs récursifs apparaît étonnamment souvent. Un exemple bien visité est quand vous devez faire correspondre quelque chose qui peut nicher, comme des parenthèses, des citations équilibrées, ou même HTML / balises XML. Voici le match pour parens balenced:

\((?:[^()]*+|(?0))*\)

Je trouve que plus délicate à lire en raison de sa nature compacte. Ceci est facilement curable avec le mode /x pour faire des espaces plus importants:

\( (?: [^()] *+ | (?0) )* \)

Là encore, puisque nous utilisons parens pour notre récursion, un exemple plus clair serait correspondant à des guillemets simples imbriqués:

‘ (?: [^‘’] *+ | (?0) )* ’

Une autre chose définie récursive vous voudrez peut-être serait un match de palindrome. Ce modèle simple fonctionne en Perl:

^((.)(?1)\2|.?)$

que vous pouvez tester sur la plupart des systèmes en utilisant quelque chose comme ceci:

$ perl -nle 'print if /^((.)(?1)\2|.?)$/i' /usr/share/dict/words

Notez que la mise en œuvre de PCRE de récursion nécessite plus complexe

^(?:((.)(?1)\2|)|((.)(?3)\4|.))

Ceci est en raison des restrictions sur la façon dont fonctionne récursives PCRE.

Une bonne Parsing

Pour moi, les exemples ci-dessus sont pour la plupart des matchs de jouets, pas tous que intéressant, vraiment. Quand il devient intéressant est quand vous avez une vraie grammaire que vous essayez d'analyser. Par exemple, RFC 5322 définit une adresse de courrier plutôt élaborée. Voici un modèle « grammaire » pour le match:

$rfc5322 = qr{

   (?(DEFINE)

     (?<address>         (?&mailbox) | (?&group))
     (?<mailbox>         (?&name_addr) | (?&addr_spec))
     (?<name_addr>       (?&display_name)? (?&angle_addr))
     (?<angle_addr>      (?&CFWS)? < (?&addr_spec) > (?&CFWS)?)
     (?<group>           (?&display_name) : (?:(?&mailbox_list) | (?&CFWS))? ; (?&CFWS)?)
     (?<display_name>    (?&phrase))
     (?<mailbox_list>    (?&mailbox) (?: , (?&mailbox))*)

     (?<addr_spec>       (?&local_part) \@ (?&domain))
     (?<local_part>      (?&dot_atom) | (?&quoted_string))
     (?<domain>          (?&dot_atom) | (?&domain_literal))
     (?<domain_literal>  (?&CFWS)? \[ (?: (?&FWS)? (?&dcontent))* (?&FWS)?
                                   \] (?&CFWS)?)
     (?<dcontent>        (?&dtext) | (?&quoted_pair))
     (?<dtext>           (?&NO_WS_CTL) | [\x21-\x5a\x5e-\x7e])

     (?<atext>           (?&ALPHA) | (?&DIGIT) | [!#\$%&'*+-/=?^_`{|}~])
     (?<atom>            (?&CFWS)? (?&atext)+ (?&CFWS)?)
     (?<dot_atom>        (?&CFWS)? (?&dot_atom_text) (?&CFWS)?)
     (?<dot_atom_text>   (?&atext)+ (?: \. (?&atext)+)*)

     (?<text>            [\x01-\x09\x0b\x0c\x0e-\x7f])
     (?<quoted_pair>     \\ (?&text))

     (?<qtext>           (?&NO_WS_CTL) | [\x21\x23-\x5b\x5d-\x7e])
     (?<qcontent>        (?&qtext) | (?&quoted_pair))
     (?<quoted_string>   (?&CFWS)? (?&DQUOTE) (?:(?&FWS)? (?&qcontent))*
                          (?&FWS)? (?&DQUOTE) (?&CFWS)?)

     (?<word>            (?&atom) | (?&quoted_string))
     (?<phrase>          (?&word)+)

     # Folding white space
     (?<FWS>             (?: (?&WSP)* (?&CRLF))? (?&WSP)+)
     (?<ctext>           (?&NO_WS_CTL) | [\x21-\x27\x2a-\x5b\x5d-\x7e])
     (?<ccontent>        (?&ctext) | (?&quoted_pair) | (?&comment))
     (?<comment>         \( (?: (?&FWS)? (?&ccontent))* (?&FWS)? \) )
     (?<CFWS>            (?: (?&FWS)? (?&comment))*
                         (?: (?:(?&FWS)? (?&comment)) | (?&FWS)))

     # No whitespace control
     (?<NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f])

     (?<ALPHA>           [A-Za-z])
     (?<DIGIT>           [0-9])
     (?<CRLF>            \x0d \x0a)
     (?<DQUOTE>          ")
     (?<WSP>             [\x20\x09])
   )

   (?&address)

}x;

Comme vous le voyez, qui est très semblable à la BNF. Le problème est qu'il est juste un match, pas une capture. Et vous ne voulez vraiment pas simplement entourer le tout avec parens capture parce que cela ne vous dit pas quelle production correspondait quelle partie. En utilisant le module Regexp :: Grammaires mentionné précédemment, nous pouvons.

#!/usr/bin/env perl

use strict;
use warnings;
use 5.010;
use Data::Dumper "Dumper";

my $rfc5322 = do {
    use Regexp::Grammars;    # ...the magic is lexically scoped
    qr{

    # Keep the big stick handy, just in case...
    # <debug:on>

    # Match this...
    <address>

    # As defined by these...
    <token: address>         <mailbox> | <group>
    <token: mailbox>         <name_addr> | <addr_spec>
    <token: name_addr>       <display_name>? <angle_addr>
    <token: angle_addr>      <CFWS>? \< <addr_spec> \> <CFWS>?
    <token: group>           <display_name> : (?:<mailbox_list> | <CFWS>)? ; <CFWS>?
    <token: display_name>    <phrase>
    <token: mailbox_list>    <[mailbox]> ** (,)

    <token: addr_spec>       <local_part> \@ <domain>
    <token: local_part>      <dot_atom> | <quoted_string>
    <token: domain>          <dot_atom> | <domain_literal>
    <token: domain_literal>  <CFWS>? \[ (?: <FWS>? <[dcontent]>)* <FWS>?

    <token: dcontent>        <dtext> | <quoted_pair>
    <token: dtext>           <.NO_WS_CTL> | [\x21-\x5a\x5e-\x7e]

    <token: atext>           <.ALPHA> | <.DIGIT> | [!#\$%&'*+-/=?^_`{|}~]
    <token: atom>            <.CFWS>? <.atext>+ <.CFWS>?
    <token: dot_atom>        <.CFWS>? <.dot_atom_text> <.CFWS>?
    <token: dot_atom_text>   <.atext>+ (?: \. <.atext>+)*

    <token: text>            [\x01-\x09\x0b\x0c\x0e-\x7f]
    <token: quoted_pair>     \\ <.text>

    <token: qtext>           <.NO_WS_CTL> | [\x21\x23-\x5b\x5d-\x7e]
    <token: qcontent>        <.qtext> | <.quoted_pair>
    <token: quoted_string>   <.CFWS>? <.DQUOTE> (?:<.FWS>? <.qcontent>)*
                             <.FWS>? <.DQUOTE> <.CFWS>?

    <token: word>            <.atom> | <.quoted_string>
    <token: phrase>          <.word>+

    # Folding white space
    <token: FWS>             (?: <.WSP>* <.CRLF>)? <.WSP>+
    <token: ctext>           <.NO_WS_CTL> | [\x21-\x27\x2a-\x5b\x5d-\x7e]
    <token: ccontent>        <.ctext> | <.quoted_pair> | <.comment>
    <token: comment>         \( (?: <.FWS>? <.ccontent>)* <.FWS>? \)
    <token: CFWS>            (?: <.FWS>? <.comment>)*
                             (?: (?:<.FWS>? <.comment>) | <.FWS>)

    # No whitespace control
    <token: NO_WS_CTL>       [\x01-\x08\x0b\x0c\x0e-\x1f\x7f]
    <token: ALPHA>           [A-Za-z]
    <token: DIGIT>           [0-9]
    <token: CRLF>            \x0d \x0a
    <token: DQUOTE>          "
    <token: WSP>             [\x20\x09]
    }x;
};

while (my $input = <>) {
    if ($input =~ $rfc5322) {
        say Dumper \%/;       # ...the parse tree of any successful match
                              # appears in this punctuation variable
    }
}

Comme vous le voyez, en utilisant une notation très légèrement différente dans le modèle, vous maintenant obtenir quelque chose qui stocke l'ensemble de l'arborescence Parse loin pour vous dans la variable %/, avec tout soigneusement étiquetés. Le résultat de la transformation est encore un modèle, comme vous pouvez le voir par l'opérateur =~. Il est juste un peu magique.

scroll top