The Perl Toolchain Summit 2025 Needs You: You can help 🙏 Learn more

# A simple grammar for GraphQL
%grammar graphql
%version 0.01
%include pegex-atoms
# inspiration drawn from the slightly obsolete https://github.com/antlr/grammars-v4/blob/master/graphql/GraphQL.g4
# in order to capture comments *before* a given rule-group *into* it,
# can't suck up whitespace *after* rule-groups as part of them
graphql: definition+
definition: (operationDefinition | fragment | typeSystemDefinition | .ws2)
operationDefinition: selectionSet | operationType .(-) name? .(-) variableDefinitions? .(-) directives? selectionSet
operationType: /(query|mutation|subscription)/
selectionSet: .(/- LCURLY -/) ( selection+ | `Expected name` ) .(/- RCURLY -/)
selection: (field | inline_fragment | fragment_spread) .(-)
field: alias? name .(-) arguments? .(-) directives? selectionSet?
alias: name .(/- COLON -/)
arguments: .(/- LPAREN -/) argument+ .(/- RPAREN -/)
argument: name .(/- COLON -/) value
fragment_spread: .spread fragmentName .(-) directives?
inline_fragment: .spread typeCondition? .(-) directives? selectionSet
fragment: .(/- 'fragment' -/) fragmentName .(-) (typeCondition | `Expected "on"`) .(-) directives? selectionSet
fragmentName: ('on' `Unexpected Name "on"` | name)
typeCondition: .(/'on' -/) namedType
value: (variable | float | int | string | boolean | null | enumValue | listValue | objectValue) .(-)
value_const: (float | int | string | boolean | null | enumValue | listValue_const | objectValue_const) .(-)
boolean: /(true|false)/
null: /(null)/
enumValue: (/(true|false|null)/ `Invalid enum value` | name)
listValue: .(/- LSQUARE -/) value* .(/- RSQUARE -/)
listValue_const: .(/- LSQUARE -/) value_const* .(/- RSQUARE -/)
objectValue: .(/- LCURLY -/) ( objectField+ | `Expected name` ) .(/- RCURLY -/)
objectValue_const: .(/- LCURLY -/) ( objectField_const+ | `Expected name or constant` ) .(/- RCURLY -/)
objectField: name .(/- COLON -/) value .(-)
objectField_const: name .(/- COLON -/) value_const
variableDefinitions: .(/- LPAREN -/) ( variableDefinition+ | `Expected $argument: Type` ) .(/- RPAREN -/)
variableDefinition: variable .(/- COLON -/) typedef defaultValue? .(-)
variable: .(/- DOLLAR/) name
defaultValue: .(/- EQUAL -/) value_const
# not "type" as using that for "objectTypeDefinition"
typedef: nonNullType | namedType | listType
namedType: name .(-)
listType: LSQUARE typedef RSQUARE
nonNullType: namedType /- BANG/ | listType /- BANG/
directives: directiveactual+
directiveactual: .(/- AT/) name arguments?
string: blockStringValue | stringValue
stringValue: /
DOUBLE
(
(:
BACK (: # Backslash escapes
[
DOUBLE # Double Quote
BACK # Back Slash
SLASH # Foreward Slash
'b' # Back Space
'f' # Form Feed
'n' # New Line
'r' # Carriage Return
't' # Horizontal Tab
]
|
'u' HEX{4} # Unicode octet pair
)
|
[^ DOUBLE CONTROLS BACK ] # Anything else
)*
)
DOUBLE
/
blockStringValue: /
DOUBLE DOUBLE DOUBLE
(
(:
(: BACK DOUBLE DOUBLE DOUBLE ) |
[^ CONTROLS DOUBLE ] |
[ TAB NL CR ] |
(: DOUBLE (?!"") )
)*
)
DOUBLE DOUBLE DOUBLE
/
float: /(
DASH?
(: 0 | [1-9] DIGIT* )
(:
# one or other or both. not neither
(: DOT DIGIT+ ) (: [eE] [ DASH PLUS ]? DIGIT+ ) |
(: DOT DIGIT+ ) |
(: [eE] [ DASH PLUS ]? DIGIT+ )
)
)/
int: /(
DASH?
(: 0 | [1-9] DIGIT* )
)/
name: /([ UNDER ALPHAS ] [ WORDS ]*)/
spread: /\.{3}/ .(-)
ws: / (: WS | \x{FEFF} | COMMA | comment ) /
comment: / BLANK* HASH BLANK* [^\r\n]* (: EOL | CR !NL | EOS ) / # CR is because MacOS 9
description: .(/ [ WS NL ]* /) string .(/ [ WS NL ]* /) | .(-)
typeSystemDefinition: description? (schema | typeDefinition | typeExtensionDefinition | directive)
schema: .(/'schema' -/) directives? ( .(/- LCURLY -/) operationTypeDefinition+ .(/- RCURLY /) )?
operationTypeDefinition: operationType .(/- COLON -/) namedType .(-)
typeDefinition: scalar | type | interface | union | enumTypeDefinition | input
# aka scalarTypeDefinition
scalar: .(/'scalar' -/) name .(-) directives?
# aka objectTypeDefinition
type: .(/'type' -/) name .(-) implementsInterfaces? .(-) directives? ( .(/- LCURLY /) fieldDefinition+ .(/- RCURLY /) )?
implementsInterfaces: .(/'implements' -/) .(/- AMP -/)? (namedType+ % .(/- AMP -/))
fieldDefinition: description? name .(-) argumentsDefinition? .(/- COLON -/) typedef .(-) directives?
argumentsDefinition: .(/- LPAREN /) inputValueDefinition+ .(/- RPAREN /)
inputValueDefinition: description? name .(/- COLON -/) typedef .(-) defaultValue? .(-) directives? .(-)
interface: .(/'interface' -/) name .(-) directives? ( .(/- LCURLY /) fieldDefinition+ .(/- RCURLY /) )?
union: .(/'union' -/) name .(-) directives? ( .(/- EQUAL -/) unionMembers )?
unionMembers: .(/- PIPE -/)? namedType+ % .(/- PIPE -/)
enumTypeDefinition: .(/'enum' -/) name .(-) directives? ( .(/- LCURLY /) enumValueDefinition+ .(/- RCURLY /) )?
enumValueDefinition: description? enumValue (.(-) directives)?
input: .(/'input' -/) name .(-) directives? ( .(/- LCURLY /) inputValueDefinition+ .(/- RCURLY /) )?
typeExtensionDefinition: .(/'extend' -/) (schema | typeDefinition)
directive: .(/'directive' - AT -/) name .(-) argumentsDefinition? .(/- 'on' -/) directiveLocations
directiveLocations: .(/- PIPE -/)? name+ % .(/- PIPE -/)