Как решить проблему с ключевыми словами в качестве идентификаторов в грамматическом наборе

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

type type implements type {}

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

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 ответ

Как только ваша грамматика видит 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 на самом деле не является неоднозначной; в любом контексте, где разрешен идентификатор в свободной форме, перед "стоит пунктуация" ключевое слово "может появиться снова, и в подобных случаях есть однозначные списки не совсем ключевых слов, которые не пересекаются.)

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