Como se ha comentado en la sección 31.2.5 Perl 5.10 permite el reconocimiento de expresiones definidas mediante gramáticas recursivas, siempre que estas puedan ser analizadas por un analizador recursivo descendente. Sin embargo, las expresiones regulares Perl 5.10 hace difícil construir una representación del árbol de análisis sintáctico abstracto. Además, la necesidad de explicitar en la regexp los blancos existentes entre los símbolos hace que la descripción sea menos robusta y menos legible.
El siguiente ejemplo muestra una expresión regular que traduce expresiones de diferencias en infijo a postfijo.
Se usa una variable $tran
para calcular la traducción de
la subexpresión vista hasta el momento.
La gramática original que consideramos es recursiva a izquierdas:
exp -> exp '-' digits | digitsaplicando las técnicas explicadas en 33.8.1 y en el nodo de perlmonks 553889 transformamos la gramática en:
exp -> digits rest rest -> '-' rest | # empty
Sigue el código:
pl@nereida:~/Lperltesting$ cat -n infixtopostfix.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 2 use v5.10; 3 4 # Infix to postfix translator using 5.10 regexp 5 # original grammar: 6 # exp -> exp '-' digits 7 # | digits 8 # 9 # Applying left-recursion elimination we have: 10 # exp -> digits rest 11 # rest -> '-' rest 12 # | # empty 13 # 14 my $input; 15 local our $tran = ''; 16 17 my $regexp = qr{ 18 (?&exp) 19 20 (?(DEFINE) 21 (?<exp> ((?&digits)) \s* (?{ $tran .= "$^N "; say "tran=$tran"; }) (?&rest) 22 (?{ 23 say "exp -> digits($^N) rest"; 24 }) 25 ) 26 27 (?<rest> \s* - ((?&digits)) (?{ $tran .= "$^N - "; say "tran=$tran"; }) (?&rest) 28 (?{ 29 say "rest -> - digits($^N) rest"; 30 }) 31 | # empty 32 (?{ 33 say "rest -> empty"; 34 }) 35 ) 36 37 (?<digits> \s* (\d+) 38 ) 39 ) 40 }xms; 41 42 $input = <>; 43 chomp($input); 44 if ($input =~ $regexp) { 45 say "matches: $&\ntran=$tran"; 46 } 47 else { 48 say "does not match"; 49 }La variable
$^N
contiene el valor que casó con el último paréntesis.
Al ejecutar el código anterior obtenemos:
Véase la ejecución:
pl@nereida:~/Lperltesting$ ./infixtopostfix.pl ab 5 - 3 -2 cd; tran= 5 tran= 5 3 - tran= 5 3 - 2 - rest -> empty rest -> - digits(2) rest rest -> - digits( 3) rest exp -> digits( 5) rest matches: 5 - 3 -2 tran= 5 3 - 2 -
Como se ve, el recorrido primero profundo se traduce en la reconstrucción de una derivación a derechas.
Es difícil extender el ejemplo anterior a lenguajes mas complejos debido a la
limitación de que sólo se dispone de acceso al último paréntesis vía $^N
.
En muchos casos es necesario poder acceder a paréntesis/atributos anteriores.
El siguiente código considera el caso de expresiones con sumas, restas, multiplicaciones
y divisiones. Utiliza la variable op
y una acción intermedia (líneas 51-53)
para almacenar el segundo paréntesis necesitado:
pl@nereida:~/Lperltesting$ cat -n ./calc510withactions3.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 2 use v5.10; 3 4 # Infix to postfix translator using 5.10 regexp 5 # Original grammar: 6 7 # exp -> exp [-+] term 8 # | term 9 # term -> term [*/] digits 10 # | digits 11 12 # Applying left-recursion elimination we have: 13 14 # exp -> term re 15 # re -> [+-] term re 16 # | # empty 17 # term -> digits rt 18 # rt -> [*/] rt 19 # | # empty 20 21 22 my $input; 23 my @stack; 24 25 local our $op = ''; 26 my $regexp = qr{ 27 (?&exp) 28 29 (?(DEFINE) 30 (?<exp> (?&term) (?&re) 31 (?{ say "exp -> term re" }) 32 ) 33 34 (?<re> \s* ([+-]) (?&term) \s* (?{ push @stack, $^N }) (?&re) 35 (?{ say "re -> [+-] term re" }) 36 | # empty 37 (?{ say "re -> empty" }) 38 ) 39 40 (?<term> ((?&digits)) 41 (?{ # intermediate action 42 push @stack, $^N 43 }) 44 (?&rt) 45 (?{ 46 say "term-> digits($^N) rt"; 47 }) 48 ) 49 50 (?<rt> \s*([*/]) 51 (?{ # intermediate action 52 local $op = $^N; 53 }) 54 ((?&digits)) \s* 55 (?{ # intermediate action 56 push @stack, $^N, $op 57 }) 58 (?&rt) # end of <rt> definition 59 (?{ 60 say "rt -> [*/] digits($^N) rt" 61 }) 62 | # empty 63 (?{ say "rt -> empty" }) 64 ) 65 66 (?<digits> \s* \d+ 67 ) 68 ) 69 }xms; 70 71 $input = <>; 72 chomp($input); 73 if ($input =~ $regexp) { 74 say "matches: $&\nStack=(@stack)"; 75 } 76 else { 77 say "does not match"; 78 }
Sigue una ejecución:
pl@nereida:~/Lperltesting$ ./calc510withactions3.pl 5-8/4/2-1 rt -> empty term-> digits(5) rt rt -> empty rt -> [*/] digits(2) rt rt -> [*/] digits(4) rt term-> digits(8) rt rt -> empty term-> digits(1) rt re -> empty re -> [+-] term re re -> [+-] term re exp -> term re matches: 5-8/4/2-1 Stack=(5 8 4 / 2 / - 1 -)
Sigue una solución alternativa que obvia la necesidad de introducir
incómodas acciones intermedias. Utilizamos
las variables @-
y @+
:
Since Perl 5.6.1 the special variables@-
and@+
can functionally replace$`
,$&
and$'
. These arrays contain pointers to the beginning and end of each match (see perlvar for the full story), so they give you essentially the same information, but without the risk of excessive string copying.
Véanse los párrafos en las páginas
, ) y
para mas información sobre @-
y @+
.
Nótese la función rc
en las líneas
21-28. rc(1)
nos retorna lo que casó con el último paréntesis,
rc(2)
lo que casó con el penúltimo, etc.
pl@nereida:~/Lperltesting$ cat -n calc510withactions4.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 2 use v5.10; 3 4 # Infix to postfix translator using 5.10 regexp 5 # Original grammar: 6 7 # exp -> exp [-+] term 8 # | term 9 # term -> term [*/] digits 10 # | digits 11 12 # Applying left-recursion elimination we have: 13 14 # exp -> term re 15 # re -> [+-] term re 16 # | # empty 17 # term -> digits rt 18 # rt -> [*/] rt 19 # | # empty 20 21 sub rc { 22 my $ofs = - shift; 23 24 # Number of parenthesis that matched 25 my $np = @-; 26 # $_ contains the string being matched 27 substr($_, $-[$ofs], $+[$np+$ofs] - $-[$ofs]) 28 } 29 30 my $input; 31 my @stack; 32 33 my $regexp = qr{ 34 (?&exp) 35 36 (?(DEFINE) 37 (?<exp> (?&term) (?&re) 38 (?{ say "exp -> term re" }) 39 ) 40 41 (?<re> \s* ([+-]) (?&term) \s* (?{ push @stack, rc(1) }) (?&re) 42 (?{ say "re -> [+-] term re" }) 43 | # empty 44 (?{ say "re -> empty" }) 45 ) 46 47 (?<term> ((?&digits)) 48 (?{ # intermediate action 49 push @stack, rc(1) 50 }) 51 (?&rt) 52 (?{ 53 say "term-> digits(".rc(1).") rt"; 54 }) 55 ) 56 57 (?<rt> \s*([*/]) ((?&digits)) \s* 58 (?{ # intermediate action 59 push @stack, rc(1), rc(2) 60 }) 61 (?&rt) # end of <rt> definition 62 (?{ 63 say "rt -> [*/] digits(".rc(1).") rt" 64 }) 65 | # empty 66 (?{ say "rt -> empty" }) 67 ) 68 69 (?<digits> \s* \d+ 70 ) 71 ) 72 }xms; 73 74 $input = <>; 75 chomp($input); 76 if ($input =~ $regexp) { 77 say "matches: $&\nStack=(@stack)"; 78 } 79 else { 80 say "does not match"; 81 }
Ahora accedemos a los atributos asociados con los dos paréntesis,
en la regla de <rt>
usando la función rc
:
(?<rt> \s*([*/]) ((?&digits)) \s* (?{ # intermediate action push @stack, rc(1), rc(2) })
Sigue una ejecución del programa:
pl@nereida:~/Lperltesting$ ./calc510withactions4.pl 5-8/4/2-1 rt -> empty term-> digits(5) rt rt -> empty rt -> [*/] digits(2) rt rt -> [*/] digits(4) rt term-> digits(8) rt rt -> empty term-> digits(1) rt re -> empty re -> [+-] term re re -> [+-] term re exp -> term re matches: 5-8/4/2-1 Stack=(5 8 4 / 2 / - 1 -) pl@nereida:~/Lperltesting$
Una nueva solución: dar nombre a los paréntesis y acceder a los mismos:
47 (?<rt> \s*(?<op>[*/]) (?<num>(?&digits)) \s* 48 (?{ # intermediate action 49 push @stack, $+{num}, $+{op} 50 })
Sigue el código completo:
pl@nereida:~/Lperltesting$ cat -n ./calc510withnamedpar.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 2 use v5.10; 3 4 # Infix to postfix translator using 5.10 regexp 5 # Original grammar: 6 7 # exp -> exp [-+] term 8 # | term 9 # term -> term [*/] digits 10 # | digits 11 12 # Applying left-recursion elimination we have: 13 14 # exp -> term re 15 # re -> [+-] term re 16 # | # empty 17 # term -> digits rt 18 # rt -> [*/] rt 19 # | # empty 20 21 my @stack; 22 23 my $regexp = qr{ 24 (?&exp) 25 26 (?(DEFINE) 27 (?<exp> (?&term) (?&re) 28 (?{ say "exp -> term re" }) 29 ) 30 31 (?<re> \s* ([+-]) (?&term) \s* (?{ push @stack, $^N }) (?&re) 32 (?{ say "re -> [+-] term re" }) 33 | # empty 34 (?{ say "re -> empty" }) 35 ) 36 37 (?<term> ((?&digits)) 38 (?{ # intermediate action 39 push @stack, $^N 40 }) 41 (?&rt) 42 (?{ 43 say "term-> digits($^N) rt"; 44 }) 45 ) 46 47 (?<rt> \s*(?<op>[*/]) (?<num>(?&digits)) \s* 48 (?{ # intermediate action 49 push @stack, $+{num}, $+{op} 50 }) 51 (?&rt) # end of <rt> definition 52 (?{ 53 say "rt -> [*/] digits($^N) rt" 54 }) 55 | # empty 56 (?{ say "rt -> empty" }) 57 ) 58 59 (?<digits> \s* \d+ 60 ) 61 ) 62 }xms; 63 64 my $input = <>; 65 chomp($input); 66 if ($input =~ $regexp) { 67 say "matches: $&\nStack=(@stack)"; 68 } 69 else { 70 say "does not match"; 71 }
Ejecución:
pl@nereida:~/Lperltesting$ ./calc510withnamedpar.pl 5-8/4/2-1 rt -> empty term-> digits(5) rt rt -> empty rt -> [*/] digits(2) rt rt -> [*/] digits(4) rt term-> digits(8) rt rt -> empty term-> digits(1) rt re -> empty re -> [+-] term re re -> [+-] term re exp -> term re matches: 5-8/4/2-1 Stack=(5 8 4 / 2 / - 1 -)
Construiremos en esta sección un traductor de infijo a postfijo utilizando una aproximación general: construiremos una representación del Abstract Syntax Tree o AST (véase la sección 33.9 Árbol de Análisis Abstracto para una definición detallada de que es un árbol sintáctico).
Como la aplicación es un poco mas compleja la hemos dividido en varios ficheros. Esta es la estructura:
. |-- ASTandtrans3.pl # programa principal |-- BinaryOp.pm # clases para el manejo de los nodos del AST |-- testreegxpparen.pl # prueba para Regexp::Paren `-- Regexp `-- Paren.pm # módulo de extensión de $^N
La salida del programa puede ser dividida en tres partes. La primera muestra una antiderivación a derechas inversa:
pl@nereida:~/Lperltesting$ ./ASTandtrans3.pl 2*(3-4) factor -> NUM(2) factor -> NUM(3) rt -> empty term-> factor rt factor -> NUM(4) rt -> empty term-> factor rt re -> empty re -> [+-] term re exp -> term re factor -> ( exp ) rt -> empty rt -> [*/] factor rt term-> factor rt re -> empty exp -> term re matches: 2*(3-4)Que leída de abajo a arriba nos da una derivación a derechas de la cadena
2*(3-4)
:
exp => term re => term => factor rt => factor [*/](*) factor rt => factor [*/](*) factor => factor [*/](*) ( exp ) => factor [*/](*) ( term re ) => factor [*/](*) ( term [+-](-) term re ) => factor [*/](*) ( term [+-](-) term ) => factor [*/](*) ( term [+-](-) factor rt ) => factor [*/](*) ( term [+-](-) factor ) => factor [*/](*) ( term [+-](-) NUM(4) ) => factor [*/](*) ( factor rt [+-](-) NUM(4) ) => factor [*/](*) ( factor [+-](-) NUM(4) ) => factor [*/](*) ( NUM(3) [+-](-) NUM(4) ) => NUM(2) [*/](*) ( NUM(3) [+-](-) NUM(4) )La segunda parte nos muestra la representación del AST para la entrada dada (
2*(3-4)
):
AST: $VAR1 = bless( { 'left' => bless( { 'val' => '2' }, 'NUM' ), 'right' => bless( { 'left' => bless( { 'val' => '3' }, 'NUM' ), 'right' => bless( { 'val' => '4' }, 'NUM' ), 'op' => '-' }, 'ADD' ), 'op' => '*' }, 'MULT' ); |
La última parte de la salida nos muestra la traducción a postfijo
de la expresión en infijo
suministrada en la entrada (2*(3-4)
):
2 3 4 - *
La gramática original que consideramos es recursiva a izquierdas:
exp -> exp [-+] term | term term -> term [*/] factor | factor factor -> \( exp \) | \d+aplicando las técnicas explicadas en 33.8.2 es posible transformar la gramática en una no recursiva por la izquierda:
exp -> term restoexp restoexp -> [-+] term restoexp | # vacío term -> term restoterm restoterm -> [*/] factor restoterm | # vacío factor -> \( exp \) | \d+
Ahora bien, no basta con transformar la gramática en una equivalente. Lo que tenemos como punto de partida no es una gramática sino un esquema de traducción (véase la sección 33.7) que construye el AST asociado con la expresión. Nuestro esquema de traducción conceptual es algo así:
exp -> exp ([-+]) term { ADD->new(left => $exp, right => $term, op => $1) } | term { $term } term -> term ([*/]) factor { MULT->new(left => $exp, right => $term, op => $1) } | factor { $factor } factor -> \( exp \) { $exp } | (\d+) { NUM->new(val => $1) }
Lo que queremos conseguir un conjunto de acciones semánticas asociadas para gramática no recursiva que sea equivalente a este.
Este es el programa resultante una vez aplicadas las transformaciones. La implementación de la asociación entre símbolos y atributos la realizamos manualmente mediante una pila de atributos:
pl@nereida:~/Lperltesting$ cat -n ./ASTandtrans3.pl 1 #!/usr/local/lib/perl/5.10.1/bin//perl5.10.1 2 use v5.10; 3 use strict; 4 use Regexp::Paren qw{g}; 5 use BinaryOp; 6 7 use Data::Dumper; 8 $Data::Dumper::Indent = 1; 9 10 # Builds AST 11 my @stack; 12 my $regexp = qr{ 13 (?&exp) 14 15 (?(DEFINE) 16 (?<exp> (?&term) (?&re) 17 (?{ say "exp -> term re" }) 18 ) 19 20 (?<re> \s* ([+-]) (?&term) 21 (?{ # intermediate action 22 local our ($ch1, $term) = splice @stack, -2; 23 24 push @stack, ADD->new( {left => $ch1, right => $term, op => g(1)}); 25 }) 26 (?&re) 27 (?{ say "re -> [+-] term re" }) 28 | # empty 29 (?{ say "re -> empty" }) 30 ) 31 32 (?<term> ((?&factor)) (?&rt) 33 (?{ 34 say "term-> factor rt"; 35 }) 36 ) 37 38 (?<rt> \s*([*/]) (?&factor) 39 (?{ # intermediate action 40 local our ($ch1, $ch2) = splice @stack, -2; 41 42 push @stack, MULT->new({left => $ch1, right => $ch2, op => g(1)}); 43 }) 44 (?&rt) # end of <rt> definition 45 (?{ 46 say "rt -> [*/] factor rt" 47 }) 48 | # empty 49 (?{ say "rt -> empty" }) 50 ) 51 52 (?<factor> \s* (\d+) 53 (?{ 54 say "factor -> NUM($^N)"; 55 push @stack, bless { 'val' => g(1) }, 'NUM'; 56 }) 57 | \s* \( (?&exp) \s* \) 58 (?{ say "factor -> ( exp )" }) 59 ) 60 ) 61 }xms; 62 63 my $input = <>; 64 chomp($input); 65 if ($input =~ $regexp) { 66 say "matches: $&"; 67 my $ast = pop @stack; 68 say "AST:\n", Dumper $ast; 69 70 say $ast->translate; 71 } 72 else { 73 say "does not match"; 74 }
Cada nodo del AST es un objeto. La clase del nodo nos dice que tipo de nodo es.
Así los nodos de la clase MULT
agrupan a los nódos de multiplicación y división.
Los nodos de la clase ADD
agrupan a los nódos de suma y resta.
El procedimiento general es asociar un método translate
con cada clase de nodo.
De esta forma se logra el polimorfismo necesario: cada clase de nodo sabe como traducirse
y el método translate
de cada clase puede escribirse como
$child->translate
para cada uno
de los nodos hijos $child
. Por ejemplo, si el nodo fuera un nodo IF_ELSE
de un hipotético lenguaje de programación, se llamaría a los métodos translate
sobre sus tres hijos
boolexpr
, ifstatement
y elsestatement
.
IF_ELSE
el seudocódigo para la traducción sería algo parecido a esto:
my $self = shift; my $etiqueta1 = generar_nueva_etiqueta; my $etiqueta2 = generar_nueva_etiqueta; my $boolexpr = $self->boolexpr->translate; my $ifstatement = $self->ifstatement->translate, my $elsestatement = $self->elsestatement->translate, return << "ENDTRANS"; $boolexpr JUMPZERO $etiqueta1: $ifstatement JUMP $etiqueta2: $etiqueta1: $elsestatement $etiqueta2: ENDTRANS
Siguiendo estas observaciones el código de BinaryOp.pm
queda así:
pl@nereida:~/Lperltesting$ cat -n BinaryOp.pm 1 package BinaryOp; 2 use strict; 3 use base qw(Class::Accessor); 4 5 BinaryOp->mk_accessors(qw{left right op}); 6 7 sub translate { 8 my $self = shift; 9 10 return $self->left->translate." ".$self->right->translate." ".$self->op; 11 } 12 13 package ADD; 14 use base qw{BinaryOp}; 15 16 package MULT; 17 use base qw{BinaryOp}; 18 19 package NUM; 20 21 sub translate { 22 my $self = shift; 23 24 return $self->{val}; 25 } 26 27 1;
Véase también:
En esta solución utilizamos
las variables @-
y @+
para construir una función que nos
permite acceder a lo que casó con
los últimos paréntesis con memoria:
Since Perl 5.6.1 the special variables@-
and@+
can functionally replace$`
,$&
and$'
. These arrays contain pointers to the beginning and end of each match (see perlvar for the full story), so they give you essentially the same information, but without the risk of excessive string copying.
Véanse los párrafos en las páginas
, ) y
para mas información sobre @-
y @+
.
g(1)
nos retorna lo que casó con el último paréntesis,
g(2)
lo que casó con el penúltimo, etc.
pl@nereida:~/Lperltesting$ cat -n Regexp/Paren.pm 1 package Regexp::Paren; 2 use strict; 3 4 use base qw{Exporter}; 5 6 our @EXPORT_OK = qw{g}; 7 8 sub g { 9 die "Error in 'Regexp::Paren::g'. Not used inside (?{ code }) construct\n" unless defined($_); 10 my $ofs = - shift; 11 12 # Number of parenthesis that matched 13 my $np = @-; 14 die "Error. Illegal 'Regexp::Paren::g' ref inside (?{ code }) construct\n" unless ($np > - $ofs && $ofs < 0); 15 # $_ contains the string being matched 16 substr($_, $-[$ofs], $+[$np+$ofs] - $-[$ofs]) 17 } 18 19 1; 20 21 =head1 NAME 22 23 Regexp::Paren - Extends $^N inside (?{ ... }) constructs 24 25 =head1 SYNOPSIS 26 27 use Regexp::Paren qw{g}; 28 29 'abcde' =~ qr{(.)(.)(.) 30 (?{ print g(1)." ".g(2)." ".g(3)."\n" }) # c b a 31 (.) (?{ print g(1)." ".g(2)." ".g(3)." ".g(4)."\n" }) # d c b a 32 (.) (?{ print g(1)." ".g(2)." ".g(3)." ".g(4)." ".g(5)."\n" }) # e d c b a 33 }x; 34 35 print g(1)." ".g(2)." ".g(3)." ".g(4)." ".g(5)."\n"; # error! 36 37 =head1 DESCRIPTION 38 39 Inside a C<(?{ ... })> construct, C<g(1)> refers to what matched the last parenthesis 40 (like C<$^N>), C<g(2)> refers to the string that matched with the parenthesis before 41 the last, C<g(3)> refers to the string that matched with the parenthesis at distance 3, 42 etc. 43 44 =head1 SEE ALSO 45 46 =over 2 47 48 =item * L<perlre> 49 50 =item * L<perlretut> 51 52 =item * PerlMonks node I<Strange behavior o> C<@-> I<and> C<@+> I<in perl5.10 regexps> L<http://www.perlmonks.org/?node_id=794736> 53 54 =item * PerlMonks node I<Backreference variables in code embedded inside Perl 5.10 regexps> L<http://www.perlmonks.org/?node_id=794424> 55 56 =back 57 58 =head1 AUTHOR 59 60 Casiano Rodriguez-Leon (casiano@ull.es) 61 62 =head1 ACKNOWLEDGMENTS 63 64 This work has been supported by CEE (FEDER) and the Spanish Ministry of 65 I<Educacion y Ciencia> through I<Plan Nacional I+D+I> number TIN2005-08818-C04-04 66 (ULL::OPLINK project L<http://www.oplink.ull.es/>). 67 Support from Gobierno de Canarias was through GC02210601 68 (I<Grupos Consolidados>). 69 The University of La Laguna has also supported my work in many ways 70 and for many years. 71 72 =head1 LICENCE AND COPYRIGHT 73 74 Copyright (c) 2009- Casiano Rodriguez-Leon (casiano@ull.es). All rights reserved. 75 76 These modules are free software; you can redistribute it and/or 77 modify it under the same terms as Perl itself. See L<perlartistic>. 78 79 This program is distributed in the hope that it will be useful, 80 but WITHOUT ANY WARRANTY; without even the implied warranty of 81 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Al ejecutar perldoc Regexp::Paren
podemos ver la documentación
incluida (véase la documentación en perlpod
y perlpodspec
así como la sección La Documentación en Perl
para mas detalles):
NAME Regexp::Paren - Extends $^N inside (?{ ... }) constructs SYNOPSIS use Regexp::Paren qw{g}; 'abcde' =~ qr{(.)(.)(.) (?{ print g(1)." ".g(2)." ".g(3)."\n" }) # c b a (.) (?{ print g(1)." ".g(2)." ".g(3)." ".g(4)."\n" }) # d c b a (.) (?{ print g(1)." ".g(2)." ".g(3)." ".g(4)." ".g(5)."\n" }) # e d c b a }x; print g(1)." ".g(2)." ".g(3)." ".g(4)." ".g(5)."\n"; # error! DESCRIPTION Inside a "(?{ ... })" construct, g(1) refers to what matched the last parenthesis (like $^N), g(2) refers to the string that matched with the parenthesis before the last, g(3) refers to the string that matched with the parenthesis at distance 3, etc. SEE ALSO * perlre * perlretut * PerlMonks node *Strange behavior o* "@-" *and* "@+" *in perl5.10 regexps* <http://www.perlmonks.org/?node_id=794736> * PerlMonks node *Backreference variables in code embedded inside Perl 5.10 regexps* <http://www.perlmonks.org/?node_id=794424> AUTHOR Casiano Rodriguez-Leon (casiano@ull.es) ACKNOWLEDGMENTS This work has been supported by CEE (FEDER) and the Spanish Ministry of *Educacion y Ciencia* through *Plan Nacional I+D+I* number TIN2005-08818-C04-04 (ULL::OPLINK project <http://www.oplink.ull.es/>). Support from Gobierno de Canarias was through GC02210601 (*Grupos Consolidados*). The University of La Laguna has also supported my work in many ways and for many years. LICENCE AND COPYRIGHT Copyright (c) 2009- Casiano Rodriguez-Leon (casiano@ull.es). All rights |
Casiano Rodríguez León