Форсирование пробелов между словами в грамматике Марпы
Я пытаюсь настроить грамматику, которая требует, чтобы [\w]
символы не могут появляться непосредственно рядом друг с другом, если они не находятся в одной лексеме. То есть слова должны быть отделены друг от друга пробелом или пунктуацией.
Рассмотрим следующую грамматику:
use Marpa::R2; use Data::Dump;
my $grammar = Marpa::R2::Scanless::G->new({source => \<<'END_OF_GRAMMAR'});
:start ::= Rule
Rule ::= '9' 'september'
:discard ~ whitespace
whitespace ~ [\s]+
END_OF_GRAMMAR
my $recce = Marpa::R2::Scanless::R->new({grammar => $grammar});
dd $recce->read(\'9september');
Это успешно разбирает. Теперь я хочу изменить грамматику, чтобы разделить 9
а также september
, Я думал сделать это, введя неиспользованную лексему, которая соответствует [\w]+
:
use Marpa::R2; use Data::Dump;
my $grammar = Marpa::R2::Scanless::G->new({source => \<<'END_OF_GRAMMAR'});
:start ::= Rule
Rule ::= '9' 'september'
:discard ~ whitespace
whitespace ~ [\s]+
word ~ [\w]+ ### <== Add unused lexeme to match joined keywords
END_OF_GRAMMAR
my $recce = Marpa::R2::Scanless::R->new({grammar => $grammar});
dd $recce->read(\'9september');
К сожалению, эта грамматика не работает с:
A lexeme is not accessible from the start symbol: word
Marpa::R2 exception at marpa.pl line 3.
Хотя это можно решить с помощью lexeme default
заявление:
use Marpa::R2; use Data::Dump;
my $grammar = Marpa::R2::Scanless::G->new({source => \<<'END_OF_GRAMMAR'});
lexeme default = action => [value] ### <== Fix exception by adding lexeme default statement
:start ::= Rule
Rule ::= '9' 'september'
:discard ~ whitespace
whitespace ~ [\s]+
word ~ [\w]+
END_OF_GRAMMAR
my $recce = Marpa::R2::Scanless::R->new({grammar => $grammar});
dd $recce->read(\'9september');
Это приводит к следующему выводу:
Inaccessible symbol: word
Error in SLIF parse: No lexemes accepted at line 1, column 1
* String before error:
* The error was at line 1, column 1, and at character 0x0039 '9', ...
* here: 9september
Marpa::R2 exception at marpa.pl line 16.
То есть анализ не удался из-за того, что между 9
а также september
это именно то, что я хочу, чтобы произошло. Единственная ложка дегтя в том, что есть раздражающий Inaccessible symbol: word
сообщение на STDERR, потому что word
лексема не используется в реальной грамматике.
Я вижу это в Marpa::R2::Grammar
Я мог бы заявить word
как inaccessible_ok
в настройках конструктора, но я не могу сделать это в Marpa::R2::Scanless
,
Я также мог бы сделать что-то вроде следующего:
Rule ::= nine september
nine ~ word
september ~ word
затем использовал pause
использовать пользовательский код для проверки фактического значения лексемы и вернуть соответствующую лексему в зависимости от значения.
Каков наилучший способ построения грамматики, которая использует ключевые слова или числа и слова, но запрещает запуск смежных лексем без пробелов или знаков препинания, разделяющих их?
1 ответ
Ну, очевидное решение состоит в том, чтобы потребовать некоторый пробел между ними (на уровне G1). Когда мы используем следующую грамматику
:default ::= action => ::array
:start ::= Rule
Rule ::= '9' (Ws) 'september'
Ws ::= [\s]+
:discard ~ whitespace
whitespace ~ [\s]+
затем 9september
не удается, но 9 september
анализируется Важные моменты, на которые следует обратить внимание:
- Лексемы могут быть как отброшены, так и необходимы, если они оба являются самым длинным токеном. Вот почему
:discard
а такжеWs
Правило не мешает друг другу. Марпа не возражает против такой "двусмысленности". Ws
Правило заключено в parens, которое отбрасывает значение - чтобы сохранить результирующее дерево разбора в чистоте.- Обычно вы не хотите использовать такие трюки, как призрачные лексемы, чтобы ввести парсера в заблуждение. Таким образом, лежит поломка.
- Когда важен каждый пробел, вы можете избавиться от
:discard ~ whitespace
, Это предназначено для использования, например, для C-подобных языков, где пробел традиционно не имеет значения.