自定义解析 return 值,保留未命名的终端

customising the parse return value, retaining unnamed terminals

考虑语法:

TOP ⩴ 'x' Y 'z'
Y ⩴ 'y'

以下是如何使用各种解析器获取准确值 ["TOP","x",["Y","y"],"z"](不是手动编写,而是从语法生成):

xyz__Parse-Eyapp.eyp

%strict
%tree

%%
start:
    TOP { shift; use JSON::MaybeXS qw(encode_json); print encode_json $_[0] };
TOP:
    'x' Y 'z'   { shift; ['TOP', (scalar @_) ? @_ : undef] };
Y:
    'y' { shift; ['Y', (scalar @_) ? @_ : undef] };

%%

xyz__Regexp-Grammars.pl

use 5.028;
use strictures;
use Regexp::Grammars;
use JSON::MaybeXS qw(encode_json);
print encode_json $/{TOP} if (do { local $/; readline; }) =~ qr{
<nocontext:>
<TOP>
<rule: TOP>
    <[anon=(x)]> <[anon=Y]> <[anon=(z)]>
    <MATCH=(?{['TOP', $MATCH{anon} ? $MATCH{anon}->@* : undef]})>
<rule: Y>
    <[anon=(y)]>
    <MATCH=(?{['Y', $MATCH{anon} ? $MATCH{anon}->@* : undef]})>

}msx;

为接下来的两个解析器省略了代码。使用 Pegex,功能是通过从 Pegex::Receiver. With Marpa-R2, the customisation of the return value is quite limited 继承来实现的,但嵌套数组可以通过配置选项开箱即用。

我已经证明了所需的自定义是可能的,尽管它并不总是那么容易或直接。这些附加到规则的代码片段是 运行 作为树的组装。


parse 方法 returns 除了嵌套的 Match 对象之外别无其他,它们很笨重。他们不保留未命名的终端! (只是为了确定我在说什么:这些是 TOP 规则 RHS 处的两条数据,其值为 'x''z'。)显然只有数据出现来自命名声明符的第四个被添加到树中。

分配给匹配变量(类似于它在 Regexp-Grammars 中的工作方式)似乎没有效果。由于终端没有进入匹配变量,actions 也无济于事。

总而言之,这里是语法和普通解析值:

grammar {rule TOP { x <Y> z }; rule Y { y };}.parse('x y z')

如何从中获取值 ["TOP","x",["Y","y"],"z"]?您不允许更改规则的形状,因为这可能会破坏用户附加的语义,否则其他任何事情都是公平的。我仍然认为解决方案的关键是匹配变量,但我看不出如何。

不是完整的答案,但 Match.chunks method 为您提供了一些输入字符串标记为捕获和非捕获部分。

但是,它确实无法让您区分正则表达式中的非捕获文字和隐式匹配的空格。

您可以通过添加位置捕获来规避它,并使用 Match.caps

my $m = grammar {rule TOP { (x) <Y> (z) }; rule Y { (y) }}.parse('x y z');

sub transform(Pair $p) {
    given $p.key {
        when Int { $p.value.Str }
        when Str { ($p.key, $p.value.caps.map(&transform)).flat }
    }
}

say $m.caps.map(&transform);

这会产生

(x (Y y) z)

几乎是您想要的,只是缺少顶级 TOP(只有硬编码才能进入)。

请注意,这并未涵盖所有边缘情况;例如,当捕获被量化时,$p.value 是一个数组,而不是匹配对象,所以你需要另一个级别的 .map 在那里,但总体思路应该很清楚。