Raku By Example
View me onGitHub
## % 

'1,2,3' ~~ / \d+ % ',' /;

 my $ingredients = 'milk, flour, eggs and sugar';
 # prints "milk, flour, eggs", 如果 say $/[0] 只会打印 || , 因为[] 是非捕获组!
 $ingredients ~~ m/ [\w+]+ % [\,\s*] / && say "|$/|";
# |milk, flour, eggs|
# 这里 \w+ 匹配一个单词,并且 [\w+]+ % [\,\s*]  匹配至少一个单词,并且单词之间用逗号和任意数量的空白分隔。
 '1,2,3' ~~ / \d+ % ',' / && say "|$/|";
# |1,2,3|
# % 必须要跟在量词后面,否则报错。
# 在 [\w+] 里面 [ ] 是非捕获组 

## ---

my @a = (
     '15179736712334102823,15203307381943332421,152033062464592402',
     '11230282334151797361',
     '12334105179128237367,1152033073819433324',
     '12334105179128237367,1152033073819433324###'
);

.say if /^ [\d+]+ % ',' $ / for @a;

## caps

# caps 方法返回所有的捕获,命名的和位置的,按照它们匹配的文本在原始字符串中出现的顺序返回。
# 返回的值是一个 Pair 对象列表。键是捕获的名字或数量,键值是对应的 Match 对象。

if 'abc' ~~ m/(.) <alpha> (.) / {
            say $/.caps.WHAT; # (Parcel) 
            my @a = $/.caps;
            say @a;           # 0 => 「a」 alpha => 「b」 1 => 「c」 ( 0 => 「a」 是一个 Pair 对象 
            say " -> 这次匹配有  @a.elems() 个 Pair";
               for $/.caps {
                    say .key, ' => ', .value.Str; # 键值是 对应的Match 对象, 需要调用 Str 方法, 得到字符串.

             }
 }

 # Output:
 #          0 => a
 #      alpha => b
 #          1 => c


#  复习下 Parcel 
# Parcel 由 () <>  逗号分割的列表, 或其它引用结构
# ()
# 1,2,3
# <a b c>
# <<a b c>>
# qw/a b c/

## word boundry

my $s = 'the quick brown fox jumped over the the lazy dog';
if $s ~~ m/ << (\w+) \W+ $0 >> / { # if 不再需要圆括号
    say "Found '$0' twice in a row";
    say "Found '$/[0]' twice in a line" # $/[0]  可以简写为 $0
}

## 量词捕获

# 如果你在捕获后面加上量词,匹配对象中的对应的项是一列其它对象:

my $ingredients = 'eggs, milk, sugar and flour';

if $ingredients ~~ m/(\w+)+ % [\,\s*] \s* 'and' \s* (\w+)/ {
    say 'list: ', $/[0].join(" | ");
    say 'end: ', "$/[1]";
    say $/.elems; # 数组 $/ 中含有 2 个元素
    say $/[0].WHAT;  # ARRAY, 第一个捕获 $/[0] 其实是一个数组
    say $/[0].elems; # 3, 第一个 (\w+) 匹配了 3 次
}

# 这打印:
# list: eggs | milk | sugar
# end: flour

#  第一个捕获(\w+)被量词化了,所以$/[0]包含一列单词。代码调用 .join方法将它转换为字符串。
#  不管第一个捕获匹配了多少次(并且有$/[0]中有多少元素),第二个捕获$/[1]始终可以访问。

## 匹配对象

my $str = 'Germany was reunited on 1990-10-03, peacefully';

if $str ~~ m/ (\d**4) \- (\d\d) \- (\d\d) / {
    say $/.WHAT;  # Match
    say $/.elems; # 3
    say 'Year: ',"$/[0]";
    say 'Month: ',"$/[1]";
    say 'Day: ',"$/[2]";

    # usage as an array:
    say $/.join('-'); # prints 1990-10-03
}

# Year: 1990
# Month: 10
# Day: 03
# 1990-10-03

## 命名正则

my $s = 'the quick brown fox jumped over the the lazy dog';
my regex word { \w+ [ \' \w+ ]?              }
my regex dup  { « <danci=&word> \W+ $<danci> » } # 要使用 &name 调用正则, 就像调用子例程一样 &sub , 调用后的结果起名为 danci, 就像给子例程起名字一样
if $s ~~ m/ <dupword=&dup> / {
    say "Found '{$<dupword><danci>}' twice in a row";
    # say $/.keys(); # dupword, 获取散列的键
    say $/;
}

# 这段代码引入了一个名为 word 的正则表达式,它至少匹配一个单词字符,后面跟着一个可选的单引号和更多的单词字符。
# 另外一个名为 dup (duplcate的缩写,副本的意思)的正则包含一个单词边界锚点。

# 在正则里面,语法 <&word> 在当前词法作用域内查找名为word的正则并匹配这个正则表达式。 
# <name=&regex> 语法创建了一个叫做 name 的捕获,它记录了 &regex 匹配的内容。

# 在这个例子中,dup 调用了名为 word 正则,随后匹配至少一个非单词字符,之后再匹配相同的字符串( 前面word 正则匹配过的)一次,它以另外一个字符边界结束。这种向后引用的语法记为美元符号 $  后面跟着用尖括号包裹着捕获的名字。

# 在 if 代码块里, $<dupword> 是  $/{'dupword'} 的快捷写法。因为 $/ 是一个特殊的散列, 所以可以通过键 {'dupword'} 访问到散列的值. 它访问正则 dup 产生的匹配对象。
# dup 也有一个叫 danci 的子规则。从那个调用产生的匹配对象用 $<dupword><danci>来访问。

# 直接打印 $/ 的结果, $/ 这里又变成了一个特殊的散列, fuck, 上次它不是一个特殊的数组吗? 百变星君啊,擦!
# 

# 「the the」
#  dupword => 「the the」
#   danci => 「the」 

## ---

# 具名正则的声明
my regex number { \d+ [ \. \d+]? }  # 具名正则
my token ident  { \w+            }
my rule  alpha  { <[A..Za..z]>   }

# 1.0 通过 & 来引用
say so "12.34" ~~ &number; # true

# 2.0 在正则构造 // 里使用
say so "12.88 + 0.12" ~~ / <number> \s* '+' \s* <number> /; # true
# say so "12.88 + 0.12" ~~ / <left=.number> \s* '+' \s* <right=.number> /;
# wrong, method 'number' not found for invocant of class 'Cursor'

# 3.0 在 grammar 里面使用
grammar EquationParse {
    # 这里也不能给 number 起别名, 除非 number 是在 grammar 内部声明的
     token TOP { <number> \s* '+' \s* <number> \s* '=' \s* <number> }
}

# 等式解析
my $expr = EquationParse.parse("12.88 + 0.12 = 13.00");
say $expr;

## regex match

my @matches = @*ARGS.map( {$^þ ~~ m:g/\* ~ \* (\w+)/} );

say @matches.map: { $^þ.perl ~ "\n" };

my $a-regex = /^þ/;

say 'þor' ~~ $a-regex;

my $ð = 'ð';
my $var-regex = rx/$ð $/;
say 'buð' ~~ $var-regex;

my $d = $ð;
my $no-var-regex = /$d u/;
say 'ðu' ~~ $no-var-regex;

$_ = 'ðu';

say "Matching contextual variable";
say rx/.u/;
say m/ð./;

say "Variable that inmediately matches contextual variable";
my $not-a-var =  m/ð./;
$_ = 'another context';
say $not-a-var;

say "Matching string";
say 'ðu' ~~ rx/.u/;
say 'ðu' ~~ m/ð./;

## split on change

given 'ABBBCDEEF' {
    .comb(/\w+ % <same>/).say;
}
# A BBB C D EE F

## lookaround

# 向后查看断言 - 感兴趣的东西在 <模式> 的后面
# <?after d\:> \w+ 读作:字符串 d: 后面是一个标识符 \w+
# 或者用英文描述: after the string `d:` is a `\w+`
given '2_2020_000987 column=d:bcp_soc, timestamp=1605155065124, value=42.0' {
    .comb( / ^ [\d+]+ % '_' | <?after d\:> \w+  | <?after value\=> .*/ ).say
}

# Output: (28_2820201112122420516_000000 bcp_startSoc 64.0)

# 向前查看断言 - 感兴趣的东西在 <模式> 的前面
# \d+ <?before '|' | '='> 读作: | 或 = 符号的前面是一个数字 \d+
# 或者用英文描述: before character | or = is a number \d+ 
given 'a33|b44=c55|' {
    .comb(/ \d+ <?before '|' | '='> /).say
}

# Output: (33 44 55)