Wait the light to fall

第十二天 - 构建灵活的 grammar

焉知非鱼

圣诞老人夫人写了一个基础的 Grammar,以配合 GDPR 无知精灵从世界各地收集的有关今年 naughty 或 nice 的人的简单列表。

每个记录都是一个名称,后跟一个标签,后跟一个地址,然后是一个标签,然后是 naughty 或 nice 的评估,然后用换行符结束。

Batman 1 Batman Street, Gotham Nice
Joker 5 Joker Street, Arkham Naughty
Riddler 12 Riddler Street, Arkham Naughty
Robin 2 Robin Street, Gotham Nice

她希望将 naughty 的人排除在一个列表中,将 nice 的人过滤到另一个列表中,因为 Krampus 将在今年处理所有 naughty 的人。

S.夫人用这样的 grammar 开头:

grammar naughty-nice-list {
    token TOP { <details>+ } # Find one or more records made up of name, address, assessment (defined below)
    
    token details { <name> <address> <assessment> }  # Find the elements from below, in this order
    
    token name { .*? \t } # Find any characters up to the earliest tab
    
    token address { .*? \t } # Find any characters up to the earliest tab
    
    token assessment { Naughty|Nice \n } # Find either 'Naughty' or 'Nice' followed by a newline
}

并在列表上调用它,如下所示:

naughty-nice-list.parsefile("./list.txt");

但是,当然,她必须做一些事情将细节放入单独的列表中。

为此,她创建了一个 action 动作类:

class santa-list-actions {
    has %!filtered-lists; # Create a private hash for this class

    method show { return %!filtered-lists } # Create a method to return our hash to the user

    # This method is automatically called when the token with the same name makes a match
    method details ($/) {
        # Create an array of just the name and address matches converted to strings
        my @details.push($<name>.Str, $<address>.Str);
        # Push the @details array into an array accessed with the 'Naughty' or 'Nice' key
        # Note the curly braces to interpolate { $ } instead of .
        # Otherwise we would get literally what we typed for the hash key.
        %!filtered-lists{ $<assessment>.Str }.push(@details);
    };
};

她这样使用:

my $actions = santa-list-actions.new;
naughty-nice-list.parsefile("./list.txt", actions=>$actions); # As Mrs S. called the object 'actions', the same as the keyword, she could write :$actions instead of actions=>$actions
my %hash-naughty-nice = $actions.show;

圣诞老人非常开心,她现在有一个哈希表,其中包含 ‘Naughty’ 和 ‘Nice’ 的键,每个键都包含一系列每个人的详细信息。

但是钓鱼洞里总是有一只北极熊爪子,尽管有圣诞老人的保证,来自世界各地的精灵们并不只是 “Naughty” 或 “Nice”。他们用自己的语言说出来!

圣诞老人特别问过,但圣诞老人坚持不懈。只有 ‘Naughty’ 或 ‘Nice’。但有些列表看起来像这样。

Batman 1 Bat Street, Gotham Nice
Joker 5 Joker Street, Arkham Naughty
Riddler 12 Riddler Street, Arkham Naughty
Robin 2 Robin Street, Gotham Nice
El Gato Negro 1 Gato Street, South Texas Bueno
Victor Mancho 3 Mancho Street, New York City Malo

圣诞老人简单地认为只是对新词进行硬编码,但她知道这不是懒惰的时候。世界各地都有精灵,她需要能够进化的东西。

所以,为了现在调用她的脚本,她创建了两个数组并将它们传递给 grammar:

my @nice = ['Nice', 'Bueno'];
my @naughty = ['Naughty', 'Malo'];
naughty-nice-list.parsefile("./list.txt", args=>(@nice, @naughty), actions=>$actions);

她改变了这样的 grammar 来使用新的数组:

grammar naughty-nice-list {
    token TOP (@*nice-words, @*naughty-words) { <details>+ } # Create dynamic arrays with the passed in arrays, available throughout the grammar
    token details { <name> <address> <assessment> }
    token name { .*? \t }
    token address { .*? \t }
    token assessment { @*naughty-words|@*nice-words \n } # Find either a word from the naughty-words array or from the nice-words array followed by a newline
}

但是S.太太意识到她现在最终会在她的 action 动作类中的哈希表中创建许多不同的键。 键将是 ‘Nice’,‘Naughty’,‘Bueno’ 或 ‘Malo’,因为这些将是 $ 可能拥有的匹配单词(未来有更多可能出现)。

因此,她进行了另一项更改,为评估令牌命名语法中的潜在匹配:

token assessment { $<Naughty>=@*naughty-words|$<Nice>=@*nice-words \n } # Mrs S. has now added names to the potential matches

在 action 动作类中,必须进行更改以适应这种情况。使用 makemade,圣诞老人将存储相应匹配的名称:

class santa-list-actions {
    has %!filtered-lists;

    method show { return %!filtered-lists };

    method details ($/) {
        my @details.push($<name>.Str, $<address>.Str);
        %filtered-lists{ $<assessment>.made }.push(@details); # This will now use the value from 'assessment.made' as the key, rather than the match in 'assessment.Str'
    };

    method assessment ($/) {
        if $<Naughty> { # If the named pattern 'Naughty' matched...
            make "Naughty" # ... set assessment.made to "Naughty"
        } elsif $<Nice>; { # Or if the named pattern 'Nice' matched...
            make "Nice" # ... set assessment.made to "Nice"
        };
     };
};

一旦圣诞老人将数据捕获到她自己的哈希中,她就可以轻松地检查出今年已经被马洛的 Victor Mancho 将其列入正确的列表:

say %hash-naughty-nice<Naughty>[2][0]; # Produces the output 'Victor Mancho'

所以现在,圣诞老人可以将 “Naughty” 或 “Nice” 的任何新翻译添加到相关数组,而不会触及 grammar。

圣诞老人发现自己对 Raku grammar 的灵活性非常满意。圣诞老人对这个问题的研究起初不那么重要……但是她知道她在确保每个人都能得到一个礼物或者在这个圣诞节的窗户上扔蛋的方法上做得很好。