Условная замена подвыражений с использованием регулярных выражений - PullRequest
7 голосов
/ 30 октября 2019

У меня есть текстовый ввод, аналогичный показанному ниже. Я хотел бы добавить слово auto перед каждым шаблоном 'a = b', но только если оно является частью последовательности, следующей за ключевым словом kywrd (через точку с запятой).

kywrd a=b;c=d;
e=f;
fnctn z;
g=h;

Поэтому вывод, который я ищу здесь:

kywrd2 auto a=b;auto c=d;
auto e=f;
fnctn z;
g=h;

В приведенном ниже коде Perl6 (Raku?) Используется регулярное выражение для добавления ключевого слова auto, но только до first a=b шаблон. Есть ли простой способ выполнить подстановку для всех шаблонов в последовательности;оставив g=h; неизменным?

my Str $x = slurp "in.q";
$x ~~ s:g /kywrd\s+(\w+)\=(\w+)\;/kywrd2 auto $0=$1\;/;
spurt "out.q", $x;

Ответы [ 3 ]

5 голосов
/ 30 октября 2019

В одну сторону:

# Create a separate named regex that captures an `x=y;` pair:
my regex pair { (\w+) \= (\w+) \; (\s*) }
# (Capture `(\s*)` so formatting between pairs is retained)

# Generate and return 'auto'-ized replacement of a captured pair: 
sub auto-ize ($/) { "auto $0=$1;$2" }

$x ~~ s:g { kywrd \s+ <pair>+ } = "kywrd2 $<pair>».&auto-ize.join()";

Весь код, который я показал, был бы понятен для кого-то, немного знакомого с Раку, но я все равно объясню.

  • Я вычеркнул названное регулярное выражение, чтобы соответствовать паре. (См. мой ответ на Различие в захвате и отсутствии захвата области регулярных выражений в Perl 6 / Raku , чтобы узнать, почему / как <pair> вызывает pair регулярное выражение.)

  • Подпрограмма auto-ize использует переменную соответствия ($/) в качестве аргумента. Это удобно, потому что $0 и т. Д. Затем автоматически связываются с пронумерованными захватами, связанными с переданным объектом сопоставления.

  • Я использовал синтаксис вида s [ ... ] = " ... ", потому что я думаюэто более читабельно для этого варианта использования. (См. Упоминание «различных разделителей» в s/// doc .)

  • Строка «kywrd2 ...» будет повторно оценена и станет заменойсовпадение, один раз для каждого совпадения из множества s:g совпадений.

  • Бит $<pair>».&auto-ize.join() - это код , интерполированный по правилам строк в двойных кавычках.

  • $<pair> - это сокращение от $/<pair>, т.е. клавиша <pair> от $/. Это относится к именованному захвату pair, связанному с переменной соответствия . Последнее будет соответствовать каждому совпадению из нескольких s:g совпадений по очереди.

  • Квантор + в выражении регулярного выражения <pair>+ означает, что при совпадении он производитList объектов захвата (сопоставления), а не только одного (как было бы в случае, если бы выражение было просто <pair> или <pair>?).

  • » обрабатывает свой операнд LHS как дерево или список (в данном случае список из одного или нескольких объектов захвата / сопоставления, по одному на пару foo=bar;...) и просматривает его элементы. Для каждого «листового» элемента » выполняет операцию справа. (» является мощным оператором, но имеет хорошие простые варианты использования, такие как этот, где он представляет собой просто удобный и компактный эквивалент цикла for. Вы можете записать его как >>, если предпочитаете ASCII.)

  • .&auto-ize вызывает подпрограмму auto-ize, как если бы это был метод, используя операнд слева от него в качестве первого аргумента.

Входные данные теста из ответа @ PolarBear:

kywrd a=b;c=d;
e=f;
fnctn z;
g=h;
k=m;
fnctn y;
kywrd m=n;
k=j;
kywrd z=a;b=i;
kywrd c=x;e=i;
z=q;
fnctn o;

Если поместить их в in.q и say, получая out.q, отобразится:

kywrd2 auto a=b;auto c=d;
auto e=f;
fnctn z;
g=h;
k=m;
fnctn y;
kywrd2 auto m=n;
auto k=j;
kywrd2 auto z=a;auto b=i;
kywrd2 auto c=x;auto e=i;
auto z=q;
fnctn o;
5 голосов
/ 30 октября 2019

Один из возможных способов удержания регулярного выражения до минимума:

sub repl ($input) 
{ 
    $input.Str
    .split(';', :skip-empty)
    .map( 'auto ' ~ * ~ ';')
    .join('')
 };

 my $foo = 'kywrd a=b;c=d;d=e;'; 
 $foo ~~ s:g /kywrd \s+ (.+)/kywrds2 { repl($0) }/; 
 $foo.say;

Лично я бы предпочел метод subst, а не оператор s//.

$foo .= subst(/ kywrd \s+ (.+) /, "kywrds2 { repl($0) }", :g); 
3 голосов
/ 03 ноября 2019

Не очень элегантный, но работоспособный код (древний способ)

#!/usr/bin/perl

use strict;
use warnings;

OUTER: while(<DATA>) {
    if( s/kywrd /kywrd2 / ) {
        do {
            if( ! s/(\w+)=(\w+)/auto $1=$2/g ) {
                print;
                next OUTER;
            }
            print;
        } while ( <DATA> );
    } else {
        print;
    }
}

__DATA__
kywrd a=b;c=d;
e=f;
fnctn z;
g=h;
k=m;
fnctn y;
kywrd m=n;
k=j;
kywrd z=a;b=i;
kywrd c=x;e=i;
z=q;
fnctn o;

Мне нужно взглянуть на Раку - что это за животное .

...