This is very easy to do in Perl regular expressions. All you do is insert your event callouts at the appropriate point in the pattern in the most straightforward manner imaginable.
First, imagine a pattern for pulling out decimal numbers from string:
my $rx0 = /[+-]?(?:\d+(?:\.\d*)?|\.\d+)/;
Let’s expand that out so we can insert our callouts:
my $rx1 = qr{
[+-] ?
(?: \d+
(?: \. \d* ) ?
|
\. \d+
)
}x;
For callouts, I’ll just print some debugging, but you could do anything you want:
my $rx2 = qr{
(?: [+-] (?{ say "\tleading sign" })
) ?
(?: \d+ (?{ say "\tinteger part" })
(?: \. (?{ say "\tinternal decimal point" })
\d* (?{ say "\toptional fractional part" })
) ?
|
\. (?{ say "\tleading decimal point" })
\d+ (?{ say "\trequired fractional part" })
) (?{ say "\tsuccess" })
}x;
Here’s the whole demo:
use 5.010;
use strict;
use utf8;
my $rx0 = qr/[+-]?(?:\d+(?:\.\d*)?|\.\d+)/;
my $rx1 = qr{
[+-] ?
(?: \d+
(?: \. \d* ) ?
|
\. \d+
)
}x;
my $rx2 = qr{
(?: [+-] (?{ say "\tleading sign" })
) ?
(?: \d+ (?{ say "\tinteger part" })
(?: \. (?{ say "\tinternal decimal point" })
\d* (?{ say "\toptional fractional part" })
) ?
|
\. (?{ say "\tleading decimal point" })
\d+ (?{ say "\trequired fractional part" })
) (?{ say "\tsuccess" })
}x;
my $string = <<'END_OF_STRING';
The Earth’s temperature varies between
-89.2°C and 57.8°C, with a mean of 14°C.
There are .25 quarts in 1 gallon.
+10°F is -12.2°C.
END_OF_STRING
while ($string =~ /$rx2/gp) {
printf "Number: ${^MATCH}\n";
}
which when run produces this:
leading sign
integer part
internal decimal point
optional fractional part
success
Number: -89.2
integer part
internal decimal point
optional fractional part
success
Number: 57.8
integer part
success
Number: 14
leading decimal point
leading decimal point
required fractional part
success
Number: .25
integer part
success
Number: 1
leading decimal point
leading sign
integer part
success
Number: +10
leading sign
integer part
internal decimal point
optional fractional part
success
Number: -12.2
leading decimal point
You may want to arrange a more grammatical regular expression for maintainability. This also helps for when you want to make a recursive descent parser out of it. (Yes, of course you can do that: this is Perl, after all. :)
Look at the last solution in this answer for what I mean by grammatical regexes. I also have larger examples elsewhere here on SO.
But it sounds like you should look at the Regexp::Grammars
module by Damian Conway, which was built for just this sort of thing. This question talks about it, and has a link to the module proper.