Марпа: Могу ли я явно запретить ключевые слова как идентификаторы?

Я внедряю новый 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 Я немного изменил приведенный выше скрипт, чтобы привести пример - код, вывод.

Другие вопросы по тегам