I like how Raku has redesigned the regular expression syntax.
It is so much cleaner, extensible, and frankly more powerful.
Here is a direct translation of your regex into a Raku regular expression.
/
:sigspace
^
[
[(\d+) ':']?
[(\d+) ':']
]?
(\d+)
[
<[,.]> (\d+)
]?
$
/;
The most noticeable thing is of course the whitespace.
You don't realize how nice adding a few spaces can be until you are prevented from doing so.
Perhaps the second most noticeable in the above translation is :sigspace
.
The thing that really makes it noticeable is that I didn't have to write a single \s*
even though it works as if I had.
What :sigspace
does is translate whitespace in the regular expression into allowing whitespace in the string to be matched.
(Normally whitespace in a regex does nothing.)
Below is a short example of its use.
The first line uses :sigspace
.
The second line is the direct translation of what it compiles into.
The third line is the traditional way you would write it.
' 123 456 ' ~~ /:s ^ (\d+) (\d+) $/
' 123 456 ' ~~ /^ <.ws> (\d+) <.ws> (\d+) <.ws> $/
' 123 456 ' ~~ /^ \s* (\d+) \s+ (\d+) \s* $/
The <ws>
token is smart. It knows if whitespace should be optional or mandatory.
Between (\d+)
and (\d+)
it is mandatory because otherwise you wouldn't be able to tell where one ends and the other starts.
(The .
in <.ws>
is there to indicate it isn't important enough to be included in the match result object.)
The next thing is that (?:…)
has been renamed to […]
.
In fact every (?SOMETHING … )
is gone.
(?i: … ) → [ :i … ] or [ :ignorecase … ]
(?-i: … ) → [ :!i … ] or [ :!ignorecase … ]
(?<= … ) → <?after … >
(?<! … ) → <!after … >
(?= … ) → <?before … >
(?! … ) → <!before … >
(?> \w+ ) → \w+:
→ # see also `:ratchet` mode
(?(condition)yes-regexp|no-regexp)
→ [ <?{ condition }> yes-regexp || no-regexp ]
(?<named> …) → $<named> = …
Note that something like <foo>
is a function call.
And <?foo>
is a function call where only the truthiness of the result matters.
So then <!foo>
must be a function call where only the untruthiness of the result matters.
The (?(condition)yes-regexp|no-regexp)
wasn't really replaced with anything.
The replacement code is just the combination of features that happen to work together to make adding such a feature uneccesary.
:ignorecase
is an adverb that turns on ignorecase mode.
Adverbs are something that are everywhere in Raku.
(It doesn't need to be in []
unless you need to constrain it to a small part of your regex.)
Something which you probably won't notice until it is pointed out, is that regular expressions are code.
It is just code with a different default syntax and semantics.
'AAABBBCCC' ~~ /
^
$<a> = ('A'+:)
{}
:my $count = $<a>.chars; # <-- this is a regular variable
$<b> = ('B' ** {$count})
$<c> = ('C' ** {$count})
$
/
Since it is just code, you can embed or call out to regular Raku code to add or extend any feature you may want.
What that means is all of the weird extensions to regexs that other languages have added are unnecessary.
(They may get added with a nice syntax if they are useful enough.)
For more complex regular expressions, you can create a grammar.
grammar Foo {
rule TOP {^
<colon-part> <point-part>?
}
rule colon-part {
<num> ** 1..3 % ':'
# <num> repeated 1..3 times, each separated by ':'
}
rule point-part {
<[.,]> <num>
}
token num { \d+ }
}
say Foo.parse( '1:2:3.4' );
「1:2:3.4」
colon-part => 「1:2:3」
num => 「1」
num => 「2」
num => 「3」
point-part => 「.4」
num => 「4」
Which is roughly the same structure as:
{
'colon-part' => {
'num' => [ '1', '2', '3' ],
}
'point-part' => {
'num' => '4',
}
}
The syntax is such that you could create that same multi-level match data structure using a single regex.
/:s ^
$<colon-part> = (
[$<num> = (\d+)] ** 1..3 % ':'
)
$<point-part> = (
<[.,]> $<num> = (\d+)
)?
$
/
Note that (…)
creates a new scope, while […]
doesn't.
You can also combine segments.
my $num = /\d+/;
my $colon-part = /:s <num=$num> ** 1..3 % ':' /;
my $point-part = /:s <[.,]> <num=$num> /;
'1:2:3.4' ~~ /:s ^ <colon-part=$colon-part> <point-part=$point-part>? $/
The reason for <num=$num>
is so that the result of the regex in $num
gets stored under the name of num
in the result object.
Otherwise it could have just been <$num>
.
You could of course just put the parts of the grammar into the lexical namespace.
my token num {\d+}
my rule colon-part { <num> ** 1..3 % ':' }
my rule point-part {:s <[.,]> <num> }
'1:2:3.4' ~~ /:s ^ <colon-part> <point-part>? $/