匹配任意分隔符

时间:2016-02-11 16:04:39

标签: perl parsing marpa

我之前使用Marpa解析复杂而愚蠢的旧文本格式取得了很好的成功,我正在尝试再次这样做。

这种特殊格式包含数百种不同类型的“开始”和“结束”块,如下所示:

Begin BlahBlah
    asdf qwer 123
    987 xxxx
End BlahBlah

Begin FooFoo
    Begin BarBar
        some stuff (1,2,3)
    End BarBar
    whatever x
End FooFoo

如何在上面的内容中创建一个与所有BlahBlah,BarBar和FooFoo相匹配的规则?我在任何示例中都没有看到如何动态捕获令牌并重新使用它来终止规则,至少不是标准的无扫描语法示例。我不想列举所有不同类型的块,因为新类型会破坏事物,我不认为它是必要的。

Begin / End块的内容对于这个问题并不重要。实际上,这些东西是一个复杂的混乱,但我不知道如何通过。我正在挥手让其他复杂的细节让Marpa成为一个很好的工具,这样我就不想诉诸正则表达式了。

我想要实现的最低限度是块类型(即“BlahBlah”)的键值映射,其内容为字符串。

1 个答案:

答案 0 :(得分:1)

这并没有完全回答我原来的问题,因为我最终只是忽略了" End"之后的重复字符串。令牌。我可能会遵循上面的评论建议,只需检查开始/结束名称是否在后处理步骤中匹配。在假设令牌是多余的情况下操作,这似乎工作正常,作为粗略的第一次切割。批评欢迎:

#!/usr/bin/perl
use warnings;
use strict;
use v5.18;
use utf8;
use feature 'unicode_strings';
use autodie;

use Marpa::R2;
use Data::Dumper;

my $g = Marpa::R2::Scanless::G->new({
        source         => \(<<'END_OF_SOURCE'),
lexeme default = latm => 1
:default ::= action => ::array
:start ::= beginend_blocks
:discard ~ <ws>

beginend_blocks ::= beginend_block+

beginend_block ::= beginend_block_header beginend_block_contents

beginend_block_header ::= ('Begin') beginend_block_name action => ::first

beginend_block_name ::= <word> 

beginend_block_contents ::= beginend_block_content_elems (beginend_block_terminator) (<word>)

beginend_block_content_elems ::= beginend_block_content_elem+
beginend_block_content_elem ::= word            action => ::first
                              | beginend_block  action => ::first

beginend_block_terminator ::= ('End')

<word> ~ <wordchar>+
<wordchar> ~ [\S]

<ws> ~ [\s]+

END_OF_SOURCE
});


my $test_str = <<THEDATA;
Begin BlahBlah
    asdf qwer 123
    987 xxxx
End BlahBlah

Begin FooFoo
    something else
    Begin BazBaz
        some stuff (1,2,3)
    End BazBaz
    whatever x
    Begin BarBar
        some stuff (1,2,3)
    End BarBar
    whatever y 
End FooFoo
THEDATA

MAIN: {
    my $re = Marpa::R2::Scanless::R->new({ grammar => $g, trace_terminals => 0 });

    for (my $pos = $re->read(\$test_str); $pos < length $test_str; $pos = $re->resume) {
        my ($pause_start, undef) = $re->pause_span;
    }

    say Dumper $re->value;
}