Марпа: Могу ли я явно запретить ключевые слова как идентификаторы?
Я внедряю новый DSL в Marpa и (из Regexp::Grammars) я более чем доволен. Мой язык поддерживает несколько унарных и двоичных операторов, объекты с идентификаторами в стиле C и вызовы методов с использованием знакомых точечных обозначений. Например:
foo.has(bar == 42 AND baz == 23)
Я обнаружил приоритетную функцию правил, предлагаемую языком описания грамматики Марпы, и стал на нее сильно полагаться, поэтому у меня есть только одно правило G1 Expression
, Выдержка (многие альтернативы и семантические действия опущены для краткости):
Expression ::=
NumLiteral
| '(' Expression ')' assoc => group
|| Expression ('.') Identifier
|| Expression ('.') Identifier Args
| Expression ('==') Expression
|| Expression ('AND') Expression
Args ::= ('(') ArgsList (')')
ArgsList ::= Expression+ separator => [,]
Identifier ~ IdentifierHeadChar IdentifierBody
IdentifierBody ~ IdentifierBodyChar*
IdentifierHeadChar ~ [a-zA-Z_]
IdentifierBodyChar ~ [a-zA-Z0-9_]
NumLiteral ~ [0-9]+
Как видите, я использую интерфейс Scanless (SLIF). Моя проблема в том, что это также анализирует, например:
foo.AND(5)
Марпа знает, что после точки может быть только идентификатор, поэтому он даже не учитывает тот факт, что AND
может быть ключевым словом. Я знаю, что могу избежать этой проблемы, выполнив отдельный этап лексического AND
как ключевое слово, но эта крошечная бумажка не стоит усилий.
Есть ли способ в SLIF ограничить Identifier
правило только для идентификаторов без ключевых слов?
2 ответа
Я не знаю, как выразить это в грамматике. Вы можете ввести промежуточный нетерминал для идентификатора, который будет проверять условие, хотя:
#!/usr/bin/perl
use warnings;
use strict;
use Syntax::Construct qw{ // };
use Marpa::R2;
my %reserved = map { $_ => 1 } qw( AND );
my $grammar = 'Marpa::R2::Scanless::G'->new(
{ bless_package => 'main',
source => \( << '__GRAMMAR__'),
:default ::= action => store
:start ::= S
S ::= Id
| Id NumLiteral
Id ::= Identifier action => allowed
Identifier ~ IdentifierHeadChar IdentifierBody
IdentifierBody ~ IdentifierBodyChar*
IdentifierHeadChar ~ [a-zA-Z_]
IdentifierBodyChar ~ [a-zA-Z0-9_]
NumLiteral ~ [0-9]+
:discard ~ whitespace
whitespace ~ [\s]+
__GRAMMAR__
});
for my $value ('ABC', 'ABC 42', 'AND 1') {
my $value = $grammar->parse(\$value, 'main');
print $$value, "\n";
}
sub store {
my (undef, $id, $arg) = @_;
$arg //= 'null';
return "$id $arg";
}
sub allowed {
my (undef, $id) = @_;
die "Reserved keyword $id" if $reserved{$id};
return $id
}
Вы можете использовать приоритеты лексемы, предназначенные именно для такого рода вещей, пример здесь в тестовом наборе Marpa::R2.
В основном, вы заявляете <AND keyword> ~ 'AND'
лексема и дать ему приоритет 1, так что это предпочтительнее, чем Identifier
, Это должно сработать.
PS Я немного изменил приведенный выше скрипт, чтобы привести пример - код, вывод.