使用Marpa解析单引号字符串:r2 perl

时间:2018-04-30 21:38:14

标签: perl grammar marpa regexp-grammars

如何使用Marpa解析单引号字符串:r2? 在我的下面的代码中,单引号字符串在解析时附加'\'。

代码:

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


my $grammar = Marpa::R2::Scanless::G->new(
   {  default_action => '[values]',
      source         => \(<<'END_OF_SOURCE'),
  lexeme default = latm => 1

:start ::= Expression

# include begin

Expression ::= Param
Param ::= Unquoted                                         
        | ('"') Quoted ('"') 
        | (') Quoted (')

:discard      ~ whitespace 
whitespace    ~ [\s]+

Unquoted      ~ [^\s\/\(\),&:\"~]+
Quoted        ~ [^\s&:\"~]+

END_OF_SOURCE
   });

my $input1 = 'foo';
#my $input2 = '"foo"';
#my $input3 = '\'foo\'';

my $recce = Marpa::R2::Scanless::R->new({ grammar => $grammar });

print "Trying to parse:\n$input1\n\n";
$recce->read(\$input1);
my $value_ref = ${$recce->value};
print "Output:\n".Dumper($value_ref);

输出的:

Trying to parse:
foo

Output:
$VAR1 = [
          [
            'foo'
          ]
        ];

Trying to parse:
"foo"

Output:
$VAR1 = [
          [
            'foo'
          ]
        ];

Trying to parse:
'foo'

Output:
$VAR1 = [
          [
            '\'foo\''
          ]
        ]; (don't want it to be parsed like this)

以上是所有输入的输出,我不希望第3个附加'\'和单引号。我希望它像OUTPUT2一样被解析。请指教。

理想情况下,它应该根据Param :: =(')Quoted(')

选择单引号之间的内容

3 个答案:

答案 0 :(得分:1)

关于Data :: Dumper输出的另一个答案是正确的。但是,你的语法不像你期望的那样工作。

解析输入'foo'时,Marpa会考虑三个Param替代方案。该位置预测的词位是:

  • Unquoted ~ [^\s\/\(\),&:\"~]+
  • '"'
  • ') Quoted ('

是的,最后一个字面意思是) Quoted (,而不是任何包含单引号的内容。

即使它是([']) Quoted ([']):由于令牌匹配最长,不带引号的词汇将匹配整个输入,包括单引号。

" foo "(带双引号)等输入会发生什么?现在,只有'"' lexeme匹配,然后任何空格都会被丢弃,然后Quoted lexeme匹配,然后丢弃任何空格,然后匹配"

为了防止这种空白跳过行为并防止不带引号的规则因LATM而被首选,将引用的字符串描述为lexemes是有意义的。例如:

Param ::= Unquoted | Quoted
Unquoted ~ [^'"]+
Quoted ~ DQ | SQ
DQ ~ '"' DQ_Body '"'  DQ_Body ~ [^"]*
SQ ~ ['] SQ_Body [']  SQ_Body ~ [^']*

这些词汇将包含任何引号和转义,因此您需要对词法内容进行后处理。您可以使用事件系统(概念上很干净,但实现起来有点麻烦)或添加在解析评估期间执行此处理的操作来执行此操作。

由于lexemes无法执行操作,因此通常最好添加代理生成:

Param ::= Unquoted | Quoted
Unquoted ~ [^'"]+
Quoted ::= Quoted_Lexeme action => process_quoted
Quoted_Lexeme ~ DQ | SQ
DQ ~ '"' DQ_Body '"'  DQ_Body ~ [^"]*
SQ ~ ['] SQ_Body [']  SQ_Body ~ [^']*

然后,行动可以执行以下操作:

sub process_quoted {
  my (undef, $s) = @_;
  # remove delimiters from double-quoted string
  return $1 if $s =~ /^"(.*)"$/s;
  # remove delimiters from single-quoted string
  return $1 if $s =~ /^'(.*)'$/s;
  die "String was not delimited with single or double quotes";
}

答案 1 :(得分:0)

您的结果不包含\',其中包含'Dumper只是将结果格式化,因此很清楚字符串内部是什么,什么不是。

您可以自己测试此行为:

use Data::Dumper;

my $tick = chr(39);
my $back = chr(92);

print "Tick Dumper: " . Dumper($tick);
print "Tick Print:  " . $tick . "\n";
print "Backslash Dumper: " . Dumper($back);
print "Backslash Print:  " . $back . "\n";

您可以在此处查看演示:https://ideone.com/d1V8OE

如果您不希望输出包含单引号,您可能需要自己从输入中删除它们。

答案 2 :(得分:0)

我对Marpa::R2并不熟悉,但您是否可以尝试对Expression规则执行操作:

Expression ::= Param action => strip_quotes

然后,实现一个简单的引用剥离器,如:

sub MyActions::strip_quotes {
    @{$_[1]}[0] =~ s/^'|'$//gr;
}