Как решить проблему с ключевыми словами в качестве идентификаторов в грамматическом наборе - PullRequest
0 голосов
/ 20 января 2019

Я пытался написать грамматику языка graphql для grammarkit, и я довольно долго застрял в проблеме неоднозначности.Ключевые слова в graphql (такие как: type, implements, scalar) также могут быть именами типов или полей.IE

type type implements type {}

Сначала я определил эти ключевые слова как tokens в bnf, но это означало бы, что приведенный выше случай недействителен.Но если я напишу эти ключевые слова напрямую, как я описываю правило, это приводит к двусмысленности в грамматике.Пример проблемы, которую я вижу на основе этой грамматики ниже, - если вы определите что-то вроде этого

directive @foo on Baz | Bar
scalar Foobar @cool

, средство просмотра PSI говорит мне, что в позиции @cool оно ожидает DirectiveAddtlLocationЭто правило, которое я даже не упоминаю в скалярном правиле.Кто-нибудь знаком с Grammarkit и сталкивался с чем-то вроде этого?Я бы очень признателен за понимание.Благодарю вас.

Вот отрывок грамматики для примера ошибки, который я упоминал выше.

{
    tokens=[
            LEFT_PAREN='('
            RIGHT_PAREN=')'
            PIPE='|'
            AT='@'
            IDENTIFIER="regexp:[_A-Za-z][_0-9A-Za-z]*"
            WHITE_SPACE = 'regexp:\s+'
    ]
}

Document ::= Definition*
Definition ::=  DirectiveTypeDef | ScalarTypeDef
NamedTypeDef ::= IDENTIFIER

// I.E. @foo @bar(a: 10) @baz
DirectivesDeclSet ::= DirectiveDecl+
DirectiveDecl ::= AT TypeName

// I.E. directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION
DirectiveTypeDef ::= 'directive' AT NamedTypeDef DirectiveLocationsConditionDef
DirectiveLocationsConditionDef ::= 'on' DirectiveLocation DirectiveAddtlLocation*
DirectiveLocation ::= IDENTIFIER
DirectiveAddtlLocation ::= PIPE? DirectiveLocation

TypeName ::= IDENTIFIER

// I.E. scalar DateTime @foo
ScalarTypeDef ::= 'scalar' NamedTypeDef DirectivesDeclSet?

1 Ответ

0 голосов
/ 20 января 2019

Как только ваша грамматика видит directive @TOKEN on IDENTIFIER, она потребляет последовательность DirectiveAddtlLocation.Каждый из них состоит из необязательного PIPE, за которым следует IDENTIFIER.Как вы заметили в своем вопросе, «ключевые слова» GraphQL - это на самом деле просто особые случаи идентификаторов.Так что, вероятно, здесь происходит то, что, поскольку вы допускаете любой токен в качестве идентификатора, scalar и Foobar оба потребляются как DirectiveAddtlLocation, и на самом деле никогда не получается увидеть ScalarTypeDef.

# Parses the same as:
directive @foo on Bar | Baz | scalar | Foobar
@cool  # <-- ?????

Вы можете обойти это, перечислив явный набор разрешенных директивных положений в вашей грамматике.(Возможно, вы даже сможете продвинуться далеко вперед, просто скопировав грамматику в Приложении B спецификации GraphQL и изменив ее синтаксис.)

DirectiveLocation ::= ExecutableDirectiveLocation | TypeSystemDirectiveLocation
ExecutableDirectiveLocation ::= 'QUERY' | 'MUTATION' | ...
TypeSystemDirectiveLocation ::= 'SCHEMA' | 'SCALAR' | ...

Теперь, когда вы приступите к анализу:

directive @foo on QUERY | MUTATION
# "scalar" is not a directive location, so the DirectiveTypeDef must end
scalar Foobar @cool

(Несмотря на то, что различие между «идентификатором» и «ключевым словом» немного странно, я уверен, что грамматика GraphQL на самом деле не является неоднозначной; в любом контексте, где произвольная формаИдентификатор разрешен, есть пунктуация, прежде чем «ключевое слово» может появиться снова, и в подобных случаях есть однозначные списки не совсем ключевых слов, которые не перекрываются.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...