Eugene Barsky
Eugene Barsky

Reputation: 5992

Building a regex with sub in Perl 6

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

Answers (1)

H&#229;kon H&#230;gland
H&#229;kon H&#230;gland

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

Related Questions