【问题标题】:Matching arbitrary delimiters匹配任意分隔符
【发布时间】:2016-05-22 12:23:27
【问题描述】:

我之前用 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?我在任何示例中都看不到如何动态捕获令牌并重新使用它来终止规则,至少在标准无扫描语法示例中没有。我不想列举所有不同种类的积木,因为新种类会破坏事物,我认为没有必要。

开始/结束块的内容对问题无关紧要。实际上,这些东西是一个复杂的混乱,但没有什么我不知道如何度过难关。我对其他使 Marpa 成为一个很好的工具的复杂细节挥手致意,因此我不想求助于正则表达式。

至少我想要实现的是块类型(即“BlahBlah”)到其内容的键值映射作为字符串。

【问题讨论】:

  • 我猜这以某种方式证明了我需要做的事情,但老实说它在我的脑海中航行:github.com/jddurand/MarpaX-Languages-XML-AST/blob/master/lib/…
  • 首先,您需要一个用于嵌套的“堆栈”——参见 perl 的 pushpop。您需要在任何给定时间查找的唯一有效End xxx 是堆栈顶部的xxx。至于“内容”,例如some stuff 成为 FooFoo 内容的一部分(以及 以及 和 BarBar)?
  • 在我脑海中,玛尔巴有两种方法。一种是对“结束”分隔符的标记使用事件,并手动确保分隔符在解析时匹配。这具有允许“快速失败”的优点。但是,如果分隔符是真正嵌套的(也就是说,它们相互跨越是无效的),您可以在解析时匹配它们,然后检查它们是否匹配评估。这可能会更好,因为它允许更好的错误消息和更多的评估。
  • 我有理由确定所有的嵌套都是“真”的,我可以在“结束”字符串之后抛出重复的标记。所有块可能都由字符串“Begin”和“End”充分分隔。我只是希望有一个可爱的句法技巧来重复标记。我想我会扔掉/忽略重复标记并希望最好,而不是诉诸词位暂停事件。
  • 我玩弄了各种关于此类技巧的想法,但从未实施过。请注意,您可以做一个混合解决方案——忽略重复标记,但保存它并在评估期间检查它是否正确——在评估该规则时,您将同时拥有两者,并且您可以发出一个非常酷的错误消息来描述不匹配完全正确。

标签: perl parsing marpa


【解决方案1】:

这并不能完全回答我最初的问题,因为我最终只是忽略了“结束”标记后面的重复字符串。我可能会遵循上面的评论建议,即在后处理步骤中简单地检查开始/结束名称是否匹配。在令牌是冗余的假设下操作,这似乎工作正常,作为粗略的第一次切割。欢迎批评:

#!/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;
}

【讨论】:

    猜你喜欢
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    • 2017-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多