Разбор строки в одинарных кавычках с использованием Marpa: r2 perl - PullRequest
0 голосов
/ 01 мая 2018

Как разобрать строку в одинарных кавычках, используя 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)

Выше приведены выходы всех входов, я не хочу, чтобы к третьему добавлялись символы «\» и одинарные кавычки. Я хочу, чтобы он анализировался как OUTPUT2. Пожалуйста, сообщите.

В идеале, он должен просто выбирать содержимое между одинарными кавычками в соответствии с Param :: = (') Quoted (')

Ответы [ 3 ]

0 голосов
/ 01 мая 2018

Я не очень знаком с Marpa::R2, но не могли бы вы попробовать использовать действие по правилу Expression:

Expression ::= Param action => strip_quotes

Затем создайте простой инструмент для удаления цитат, например:

sub MyActions::strip_quotes {
    @{$_[1]}[0] =~ s/^'|'$//gr;
}
0 голосов
/ 01 мая 2018

Другой ответ относительно вывода Data :: Dumper правильный. Однако ваша грамматика не работает так, как вы ожидаете.

Когда вы анализируете ввод 'foo', Марпа рассмотрит три варианта Param. Предсказанные лексемы в этой позиции:

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

Да, последнее буквально ) Quoted (, а не что-либо, содержащее одну кавычку.

Даже если бы это было ([']) Quoted ([']): из-за самого длинного соответствия токена лексема без кавычек будет соответствовать всему вводу, включая одинарную кавычку.

Что будет с входом типа " foo " (с двойными кавычками)? Теперь только лексема '"' будет соответствовать, тогда все пробелы будут отброшены, затем лексема в кавычках совпадет, затем все пробелы будут отброшены, а затем закрывается ".

Чтобы предотвратить такое пропуск пропуска пробелов и предотвратить предпочтение правила Unquoted из-за LATM, имеет смысл описывать строки в кавычках как лексемы. Например:

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

Эти лексемы будут включать любые кавычки и экранированные символы, поэтому вам нужно постобработать содержимое лексемы. Вы можете сделать это, используя систему событий (которая концептуально чиста, но немного громоздка в реализации), или добавить действие, которое выполняет эту обработку во время анализа разбора.

Поскольку у лексем не может быть действий, обычно лучше добавить прокси-продукт:

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";
}
0 голосов
/ 01 мая 2018

Ваш результат не содержит \', он содержит '. 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

Если вы не хотите, чтобы вывод содержал одинарные кавычки, вам, вероятно, придется удалить их из ввода самостоятельно.

...