Reputation: 5992
After learning how to pass regexes
as arguments, I've tried to build my first regex using a sub
, and I'm stuck once more. Sorry for the complex rules below, I've made my best to simplify them. I need at least some clues how to approach this problem.
The regex
should consist of alternations, each of them consisting of left
, middle
and right
, where left
and right
should come in pairs and the variant of middle
depends on which right
is chosen.
An array of Pairs
contains pairs of left
and right
:
my Pair @leftright =
A => 'a',
...
Z => 'z',
;
Middle
variants are read from a hash:
my Regex %middle =
z => / foo /,
a => / bar /,
m => / twi /,
r => / bin /,
...
;
%middle<z>
should be chosen if right
is z
, %middle<a>
— if right
is a
, etc.
So, the resulting regex should be
my token word {
| A <%middle[a]> a
| Z <%middle[z]> z
| ...
}
or, more generally
my token word {
| <left=@leftright[0].key>
<middle=%middle{@leftright[0].value}>
<right=@leftright[0].value>
| (the same for index == 1)
| (the same for index == 2)
| (the same for index == 3)
...
}
and it should match Abara
and Zfooz
.
How to build token word
(which can be used e.g. in a grammar
) with a sub
that will take every pair from @leftright
, put the suitable %middle{}
depending on the value of right
and then combine it all into one regex
?
my Regex sub sub_word(Pair @l_r, Regex %m) {
...
}
my token word {
<{sub_word(@leftright, %middle)}>
}
After the match I need to know the values of left
, middle
, and right
:
"Abara" ~~ &word;
say join '|', $<left>, $<middle>, $<right> # A|bar|a
Upvotes: 3
Views: 174
Reputation: 40718
I was not able to do this using token
yet, but here is a solution with EVAL
and Regex
(and also I am using %middle
as a hash of Str
and not a hash of Regex
):
my Regex sub build_pattern (%middle, @leftrigth) {
my $str = join '|', @leftright.map(
{join ' ',"\$<left>='{$_.key}'", "\$<middle>='{%middle{$_.value}}'", "\$<right>='{$_.value}'"});
);
my Regex $regex = "rx/$str/".EVAL;
return $regex;
}
my Regex $pat = build_pattern(%middle, @leftright);
say $pat;
my $res = "Abara" ~~ $pat;
say $res;
Output:
rx/$<left>='A' $<middle>='bar' $<right>='a'|$<left>='Z' $<middle>='foo' $<right>='z'/
「Abara」
left => 「A」
middle => 「bar」
right => 「a」
For more information on why I chose to use EVAL
, see How can I interpolate a variable into a Perl 6 regex?
Upvotes: 2