Разобрать строку в одинарных кавычках, используя Marpa:r2 perl
Как разобрать строку в одинарных кавычках, используя 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 ответа
Другой ответ относительно вывода 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";
}
Я не так знаком с Marpa::R2
, но не могли бы вы попытаться использовать действие на Expression
правило:
Expression ::= Param action => strip_quotes
Затем внедрите простой инструмент для удаления цитат, например:
sub MyActions::strip_quotes {
@{$_[1]}[0] =~ s/^'|'$//gr;
}
Ваш результат не содержит \'
, это содержит '
, 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
Если вы не хотите, чтобы вывод содержал одинарные кавычки, вам, вероятно, придется удалить их из ввода самостоятельно.