3
votes

Comment faire correspondre un groupe dans une expression régulière récursive?

J'écris une expression régulière simple qui doit recevoir une paire de coordonnées et / ou un nom de carte.

Par exemple:

use strict;
use warnings;

my $command = 'move 10 15';

$command =~ /
  (?(DEFINE)
     (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
     (?<map>      (?<mapname>[a-zA-Z]+) )
     (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
  )
  move\s+(?&commands)
/six;

while (my ($k,$v) = each %+) { print "$k $v\n" }
print "$+{x}";

Ensuite, j'ai écrit cette expression régulière:

/
  (?(DEFINE)
     (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
     (?<map>      (?<mapname>[a-zA-Z]+) )
     (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
  )
  move\s+(?&commands)
/six

Mais comment puis-je obtenir la valeur des groupes x , y et map en utilisant Perl?

J'ai essayé de plusieurs manières:

move 10 15 # should returns [[10, 15]]
move 10 15 map # should returns [[10, 15, 'map']]
move map # should returns [['map']]
move 10 15 mapA mapB # should returns [[10, 15, 'mapA'], ['mapB']] 
move 10 15 mapA mapB 33 44 # should returns [[10, 15, 'mapA'], ['mapB'], [33, 44]]
move 10 15 mapA 33 44 mapB # should returns [[10, 15, 'mapA'], [33, 44, 'mapB']]


8 commentaires

Donc votre regex fonctionne correctement et vous avez juste besoin d'accéder aux groupes?


@revo Ouais. Pour autant que j'ai testé, mon regex fonctionne, mais je ne sais pas comment accéder aux groupes


Voir ceci stackoverflow.com/questions/37369803/...


@revo J'ai essayé de l'utiliser, mais je ne peux pas. % + ne renvoie rien.


Veuillez montrer votre tentative dans votre question.


@Jan Désolé. J'ai mis à jour le code en renommant la deuxième carte pour mapname . Mais j'ai toujours le même problème


Voir aussi Regexp :: Grammars


Marpa :: R2 pourrait valoir la peine d'être envisagé


3 Réponses :


2
votes


0 commentaires

6
votes

Dans l'état actuel de la question, vous ne pouvez pas l'avoir. Le perlre parle de ce

Notez que les groupes de capture correspondant à l'intérieur de la récursivité ne sont pas accessibles après le retour de la récursivité, donc la couche supplémentaire de groupes de capture est nécessaire.

mais le motif ne peut pas être obtenu avec "une couche supplémentaire" de capture par la suite car il n'est utilisé que dans la grammaire. Vous ne pouvez avoir le tout

if ($command =~ /
        move\s+ (?<match>(?&commands))
        (?(DEFINE)
            (?<coord>    (?<x>\d+)\s+(?<y>\d+) )
            (?<map>      (?<mapname>[a-zA-Z]+) )
            (?<commands> \s* (?: (?&coord) | (?&map) ) \s* (?&commands)? )
        )
    /six)
{
    say "got: $+{match}";
}

que là où j'ai déplacé le bloc ? (DEFINED) à la fin du modèle, comme recommandé.

Notez que cela n'aurait pas de sens non plus: dans une correspondance récursive, lequel des multiples devrait-on obtenir? Vous devrez donc restructurer l'approche pour pouvoir recapturer le match que vous souhaitez; mais je ne vois pas comment faire cela si vous voulez un sous-modèle enterré si profondément.

Pour le problème tel que présenté, j'irais avec l'écriture d'un analyseur simple, sans parler d'une expression régulière fourre-tout. Ou, dans votre approche, retravaillez la correspondance pour ses parties, espérons-le beaucoup plus facilement une fois que vous l'avez.

Et puis il y a des outils puissants, comme Marpa :: R2 , Parse :: RecDescent , Regexp :: Grammars .


1 commentaires

Très merci pour la bonne réponse! Je le fais juste à des fins d'étude (et "insistance"), pour comparer les limites entre regex et un simple analyseur. Dans mon vrai projet, j'utilise un combinateur d'analyseur pour le faire.



2
votes

Comme je ne peux pas encore commenter, la solution de Stefan Becker a un défaut.

Elle échouera si une coordonnée est 0.

Voici le correctif:

#!/usr/bin/perl
use warnings;
use strict;

use Data::Dumper;

while (<DATA>) {
    my @row;
    chomp;
    if (/^move/) {
        while (/(?:(\d+)\s+(\d+))?(?:\s+([[:alpha:]]+))?/g) {
            my @match;
            push(@match, +$1, +$2) if defined $1 && defined $2;
            push(@match, $3)       if $3;
            push(@row, \@match) if @match;
        }
    }

    print "$_: ", Dumper(\@row);
}

exit 0;

__DATA__
move 10 15
move 10 15 map
move map
move 10 15 mapA mapB
move 10 15 mapA mapB 33 44
move 10 15 mapA 33 44 mapB
move 0 15 mapA 33 44 mapB


0 commentaires