use crate::lexer;
use crate::{
executable::{
storage::*, ids::*, writer::ExecutableAstWriter
},
values::{storage::*, ids::{ValueId, ConstValueId}, self},
common::{
OperationType, IdRange, WrappingType, TypeWrappers,
unquote_string, unquote_block_string, trim_block_string_whitespace
},
Span
};
grammar<'input>(input: &'input str, ast: &mut ExecutableAstWriter);
pub ExecutableDocument: () = {
<defs:ExecutableDefinition*> => {}
}
ExecutableDefinition: ExecutableDefinitionId = {
<def:OperationDefinition> => ast.operation_definition(def),
<def:FragmentDefinition> => ast.fragment_definition(def),
}
OperationDefinition: OperationDefinitionRecord = {
<description:Description?>
<operation_type_start:@L>
<operation_type:OperationType>
<operation_type_end:@R>
<name_start:@L>
<name:Name?>
<name_end:@R>
<variable_definitions:VariableDefinitions?>
<directives:Directives>
<selection_set_start:@L>
<selection_set:SelectionSet>
<selection_set_end:@R> => {
let variable_definitions = ast.variable_definition_range(variable_definitions.map(|defs| defs.len()));
OperationDefinitionRecord {
description,
operation_type,
operation_type_span: Some(Span::new(operation_type_start, operation_type_end)),
name,
name_span: Some(Span::new(name_start, name_end)),
variable_definitions,
directives,
selection_set,
selection_set_span: Span::new(selection_set_start, selection_set_end)
}
},
<selection_set_start:@L>
<selection_set:SelectionSet>
<selection_set_end:@R> => {
OperationDefinitionRecord {
description: None,
operation_type: OperationType::Query,
operation_type_span: None,
name: None,
name_span: None,
selection_set,
selection_set_span: Span::new(selection_set_start, selection_set_end),
variable_definitions: Default::default(),
directives: Default::default()
}
}
};
FragmentDefinition: FragmentDefinitionRecord = {
<description:Description?>
fragment
<name_start:@L>
<name:FragmentName>
<name_end:@R>
<type_condition_start:@L>
<type_condition:TypeCondition>
<type_condition_end:@R>
<directives:Directives>
<selection_set_start:@L>
<selection_set:SelectionSet>
<selection_set_end:@R> => {
FragmentDefinitionRecord {
description,
name,
name_span: Span::new(name_start, name_end),
type_condition,
type_condition_span: Span::new(type_condition_start, type_condition_end),
directives,
selection_set,
selection_set_span: Span::new(selection_set_start, selection_set_end)
}
}
};
Description: DescriptionId = {
<start:@L> <literal:StringValue> <end:@R> => {
ast.description(DescriptionRecord {
literal,
span: Span::new(start, end),
})
}
}
OperationType: OperationType = {
query => OperationType::Query,
mutation => OperationType::Mutation,
subscription => OperationType::Subscription
}
VariableDefinitions: Vec<()> = {
"(" <entries:VariableDefinition+> ")" => {
entries
}
}
VariableDefinition: () = {
<description:Description?>
<name_start:@L>
"$" <name:Name>
<name_end:@R>
":"
<ty:Type>
<default_value:DefaultValue?>
<directives:Directives> => {
ast.variable_definition(VariableDefinitionRecord {
description,
name,
name_span: Span::new(name_start, name_end),
ty,
default_value,
directives
});
}
}
DefaultValue: ConstValueId = {
"=" <value:ConstValue> => {
value
}
}
SelectionSet: IdRange<SelectionId> = {
"{" <selections:Selection+> "}" => {
ast.selection_set(selections)
}
}
Selection: SelectionRecord = {
<alias_start:@L>
<alias:Alias?>
<alias_end:@R>
<name_start:@L>
<name:Name>
<name_end:@R>
<arguments:Arguments?>
<directives:Directives>
<selection_set_start:@L>
<selection_set:SelectionSet?>
<selection_set_end:@R> => {
let selection_set = selection_set.unwrap_or_default();
let arguments = arguments.map(|args| ast.arguments(args)).unwrap_or_default();
SelectionRecord::Field(
ast.field_selection(FieldSelectionRecord {
alias_span: alias.as_ref().map(|_| Span::new(alias_start, alias_end)),
alias,
name,
name_span: Span::new(alias_start, alias_end),
arguments,
directives,
selection_set_span: if selection_set.is_empty() {
None
} else {
Some(Span::new(selection_set_start, selection_set_end))
},
selection_set
})
)
},
"..."
<name_start:@L>
<fragment_name:FragmentName>
<name_end:@R>
<directives:Directives> => {
SelectionRecord::FragmentSpread(
ast.fragment_spread(FragmentSpreadRecord {
fragment_name,
fragment_name_span: Span::new(name_start, name_end),
directives
})
)
},
"..."
<type_condition_start:@L>
<type_condition:TypeCondition?>
<type_condition_end:@L>
<directives:Directives>
<selection_set_start:@L>
<selection_set:SelectionSet>
<selection_set_end:@R> => {
SelectionRecord::InlineFragment(
ast.inline_fragment(InlineFragmentRecord {
type_condition_span: type_condition.as_ref().map(|_|
Span::new(type_condition_start, type_condition_end)
),
type_condition,
directives,
selection_set,
selection_set_span: Span::new(selection_set_start, selection_set_end)
})
)
}
}
Alias: StringId = {
<alias:Name> ":" => {
alias
}
}
TypeCondition: StringId = {
on <name:NamedType> => {
name
}
}
NamedType: StringId = <s:Ident> => ast.ident(s);
Type: TypeId = {
<start:@L>
"["*
<name_start:@L>
<name:NamedType>
<wrappers:WrappingType*>
<end:@R>
=> ast.type_reference(TypeRecord {
name,
name_start,
wrappers: TypeWrappers::from_iter(wrappers),
span: Span::new(start, end)
})
}
WrappingType: WrappingType = {
"!" => WrappingType::NonNull,
"]" => WrappingType::List
}
StringValue: StringLiteralId = {
<start:@L> <s:StringLiteral> =>? {
Ok(StringLiteralId::String(ast.intern_owned_string(unquote_string(s, start)?)))
},
<s:BlockStringLiteral> => {
let id = ast.block_string(unquote_block_string(s));
StringLiteralId::Block(id)
},
}
Directives: IdRange<DirectiveId> = {
<directives:Directive*> => {
ast.directive_range(Some(directives.len()))
}
}
Directive: () = {
<name_start:@L>
"@"
<name:Name>
<name_end:@R>
<arguments:Arguments?> => {
let arguments = arguments.map(|args| ast.arguments(args)).unwrap_or_default();
ast.directive(DirectiveRecord {
name,
name_span: Span::new(name_start, name_end),
arguments
});
}
}
Arguments: Vec<ArgumentRecord> = {
"(" <arguments:Argument*> ")" => arguments
}
Argument: ArgumentRecord = {
<name_start:@L>
<name:Name>
":"
<name_end:@R>
<value:Value> => {
ArgumentRecord {
name,
name_span: Span::new(name_start, name_end),
value
}
}
}
ConstValue: ConstValueId = {
<record:ConstValueRecord> => {
ast.values.const_value(record)
}
}
ConstValueRecord: ValueRecord = {
<scalar:ScalarValueRecord> => scalar,
<start:@L> "[" <values:ConstValueRecord*> "]" <end:@R> => {
let id = ast.values.list(values);
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::List(id)
}
},
<start:@L> "{" <fields:ConstObjectField*> "}" <end:@R> => {
let fields = ast.values.const_fields(fields);
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Object(fields)
}
},
}
ConstObjectField: (values::ids::StringId, Span, ConstValueId) = {
<name_start:@L> <name:Name> <name_end:@R> ":" <value:ConstValue> => {
(
values::ids::StringId::from_executable_id(name),
Span::new(name_start, name_end),
value
)
}
}
Value: ValueId = {
<record:ValueRecord> => {
ast.values.value(record)
}
}
ValueRecord: ValueRecord = {
<start:@L> "$" <name:Name> <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Variable(values::ids::StringId::from_executable_id(name))
}
},
<scalar:ScalarValueRecord> => scalar,
<start:@L> "[" <values:ValueRecord*> "]" <end:@R> => {
let id = ast.values.list(values);
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::List(id)
}
},
<start:@L> "{" <fields:ObjectField*> "}" <end:@R> => {
let fields = ast.values.fields(fields);
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Object(fields)
}
},
}
ObjectField: (values::ids::StringId, Span, ValueId) = {
<name_start:@L> <name:Name> <name_end:@R> ":" <value:Value> => {
(
values::ids::StringId::from_executable_id(name),
Span::new(name_start, name_end),
value
)
}
}
ScalarValueRecord: ValueRecord = {
<start:@L> <int:IntegerLiteral> <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Int(int.parse().unwrap())
}
},
<start:@L> <float:FloatLiteral> <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Float(float.parse().unwrap())
}
},
<start:@L> <s:StringLiteral> <end:@R> =>? {
let id = ast.intern_owned_string(unquote_string(s, start)?);
Ok(ValueRecord {
span: Span::new(start, end),
kind: ValueKind::String(values::ids::StringId::from_executable_id(id))
})
},
<start:@L> <s:BlockStringLiteral> <end:@R> => {
let id = ast.intern_owned_string(
trim_block_string_whitespace(
unquote_block_string(s)
)
);
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::String(values::ids::StringId::from_executable_id(id))
}
},
<start:@L> true <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Boolean(true)
}
},
<start:@L> false <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Boolean(false)
}
},
<start:@L> null <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Null
}
},
<start:@L> <value:EnumValue> <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Enum(values::ids::StringId::from_executable_id(value))
}
},
}
EnumValue: StringId = {
<s:RawIdent> => ast.ident(s),
schema => ast.ident("schema"),
query => ast.ident("query"),
mutation => ast.ident("mutation"),
subscription => ast.ident("subscription"),
ty => ast.ident("type"),
input => ast.ident("input"),
implements => ast.ident("implements"),
interface => ast.ident("interface"),
"enum" => ast.ident("enum"),
union => ast.ident("union"),
scalar => ast.ident("scalar"),
extend => ast.ident("extend"),
directive => ast.ident("directive"),
repeatable => ast.ident("repeatable"),
on => ast.ident("on"),
fragment => ast.ident("fragment"),
}
Name: StringId = <s:Ident> => ast.ident(s);
FragmentName: StringId = <s:RawFragmentName> => ast.ident(s);
RawFragmentName: &'input str = {
<s:RawIdent> => s,
schema => "schema",
query => "query",
mutation => "mutation",
subscription => "subscription",
ty => "type",
input => "input",
true => "true",
false => "false",
null => "null",
implements => "implements",
interface => "interface",
"enum" => "enum",
union => "union",
scalar => "scalar",
extend => "extend",
directive => "directive",
repeatable => "repeatable",
fragment => "fragment",
}
Ident: &'input str = {
<s:RawIdent> => s,
schema => "schema",
query => "query",
mutation => "mutation",
subscription => "subscription",
ty => "type",
input => "input",
true => "true",
false => "false",
null => "null",
implements => "implements",
interface => "interface",
"enum" => "enum",
union => "union",
scalar => "scalar",
extend => "extend",
directive => "directive",
repeatable => "repeatable",
on => "on",
fragment => "fragment",
}
extern {
type Location = usize;
type Error = crate::parser::AdditionalErrors;
enum lexer::Token<'input> {
"$" => lexer::Token::Dollar,
":" => lexer::Token::Colon,
"{" => lexer::Token::OpenBrace,
"}" => lexer::Token::CloseBrace,
"(" => lexer::Token::OpenParen,
")" => lexer::Token::CloseParen,
"[" => lexer::Token::OpenBracket,
"]" => lexer::Token::CloseBracket,
"!" => lexer::Token::Exclamation,
"=" => lexer::Token::Equals,
"@" => lexer::Token::At,
"&" => lexer::Token::Ampersand,
"|" => lexer::Token::Pipe,
"..." => lexer::Token::Spread,
RawIdent => lexer::Token::Identifier(<&'input str>),
StringLiteral => lexer::Token::StringLiteral(<&'input str>),
BlockStringLiteral => lexer::Token::BlockStringLiteral(<&'input str>),
FloatLiteral => lexer::Token::FloatLiteral(<&'input str>),
IntegerLiteral => lexer::Token::IntegerLiteral(<&'input str>),
// Would be nice if these could just be aliases of `Identifier` but LARLPOP doesn't
// seem to support this well: https://github.com/lalrpop/lalrpop/issues/671
schema => lexer::Token::Schema,
query => lexer::Token::Query,
mutation => lexer::Token::Mutation,
subscription => lexer::Token::Subscription,
ty => lexer::Token::Type,
input => lexer::Token::Input,
true => lexer::Token::True,
false => lexer::Token::False,
null => lexer::Token::Null,
implements => lexer::Token::Implements,
interface => lexer::Token::Interface,
"enum" => lexer::Token::Enum,
union => lexer::Token::Union,
scalar => lexer::Token::Scalar,
extend => lexer::Token::Extend,
directive => lexer::Token::Directive,
repeatable => lexer::Token::Repeatable,
on => lexer::Token::On,
fragment => lexer::Token::Fragment
}
}