use crate::lexer;
use crate::{
Span,
type_system::{
storage::*, ids::*, writer::TypeSystemAstWriter,
DirectiveLocation,
},
values::{storage::*, ids::ConstValueId, self},
common::{
OperationType, IdRange, WrappingType, TypeWrappers,
unquote_string, unquote_block_string, trim_block_string_whitespace
},
parser::AdditionalErrors
};
grammar<'input>(input: &'input str, ast: &mut TypeSystemAstWriter);
pub TypeSystemDocument: () = {
<defs:DefinitionAndDescription+> => {}
}
Description: DescriptionId = {
<start:@L> <literal:StringValue> <end:@R> => {
ast.description(DescriptionRecord {
literal,
span: Span::new(start, end),
})
}
}
DefinitionAndDescription: () = {
<description:Description?> <def:TypeSystemDefinition> => {
ast.store_description(def, description)
}
}
TypeSystemDefinition: DefinitionId = {
<def:SchemaDefinition> => ast.schema_definition(def),
<def:ScalarDefinition> => ast.scalar_definition(def),
<def:ObjectDefinition> => ast.object_definition(def),
<def:InterfaceDefinition> => ast.interface_definition(def),
<def:UnionDefinition> => ast.union_definition(def),
<def:EnumDefinition> => ast.enum_definition(def),
<def:InputObjectDefinition> => ast.input_object_definition(def),
extend <def:SchemaExtensionDefinition> => ast.schema_extension(def),
extend <def:ScalarDefinition> => ast.scalar_extension(def),
extend <def:ObjectDefinition> => ast.object_extension(def),
extend <def:InterfaceDefinition> => ast.interface_extension(def),
extend <def:UnionDefinition> => ast.union_extension(def),
extend <def:EnumDefinition> => ast.enum_extension(def),
extend <def:InputObjectDefinition> => ast.input_object_extension(def),
<def:DirectiveDefinition> => ast.directive_definition(def),
}
SchemaDefinition: SchemaDefinitionRecord = {
<start:@L> schema <directives:Directives> <root_operations:RootOperationTypeDefinitions> <end:@R> => SchemaDefinitionRecord {
description: None,
directives,
root_operations,
span: Span::new(start, end),
}
};
SchemaExtensionDefinition: SchemaDefinitionRecord = {
<start:@L> schema <directives:Directives> <root_operations:RootOperationTypeDefinitions?> <end:@R> => SchemaDefinitionRecord {
description: None,
directives,
root_operations: root_operations.unwrap_or_default(),
span: Span::new(start, end),
}
};
RootOperationTypeDefinitions: IdRange<RootOperationTypeDefinitionId> = {
"{" <root_operations:RootOperationTypeDefinition*> "}" => ast.root_operation_definitions(root_operations),
}
RootOperationTypeDefinition: RootOperationTypeDefinitionRecord = {
<start:@L> query
<operation_type_end:@R>
":"
<name_start:@L>
<name:NamedType>
<name_end:@R>
<end:@R>
=> RootOperationTypeDefinitionRecord {
operation_type: OperationType::Query,
operation_type_span: Span::new(start, operation_type_end),
named_type: name,
named_type_span: Span::new(name_start, name_end),
span: Span::new(start, end)
},
<start:@L> mutation
<operation_type_end:@R>
":"
<name_start:@L>
<name:NamedType>
<name_end:@R>
<end:@R>
=> RootOperationTypeDefinitionRecord {
operation_type: OperationType::Mutation,
operation_type_span: Span::new(start, operation_type_end),
named_type: name,
named_type_span: Span::new(name_start, name_end),
span: Span::new(start, end)
},
<start:@L> subscription
<operation_type_end:@R>
":"
<name_start:@L>
<name:NamedType>
<name_end:@R>
<end:@R>
=> RootOperationTypeDefinitionRecord {
operation_type: OperationType::Subscription,
operation_type_span: Span::new(start, operation_type_end),
named_type: name,
named_type_span: Span::new(name_start, name_end),
span: Span::new(start, end)
},
}
ScalarDefinition: ScalarDefinitionRecord = {
<start:@L> scalar
<name_start:@L>
<name:Name>
<name_end:@R>
<directives:Directives>
<end:@R>
=> ScalarDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
directives,
span: Span::new(start,end)
}
}
ObjectDefinition: ObjectDefinitionRecord = {
<start:@L> ty
<name_start:@L>
<name:Name>
<name_end:@R>
<implements:ImplementsInterfaces?>
<directives:Directives>
<fields:FieldsDefinition?>
<end:@R>
=> ObjectDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
directives,
implements_interfaces: implements.unwrap_or_default(),
fields: ast.field_definition_range(Some(fields.map(|fields| fields.len()).unwrap_or_default())),
span: Span::new(start,end)
}
};
ImplementsInterfaces: Vec<StringId> = {
<interfaces:ImplementsInterfaces> "&" <name:NamedType> => {
let mut interfaces = interfaces;
interfaces.push(name);
interfaces
},
implements "&"? <name:NamedType> => {
vec![name]
}
}
ImplementItem: StringId = {
"&" <name:NamedType> => name,
}
FieldsDefinition: Vec<()> = {
"{" <fields:FieldDefinition+> "}" => fields
};
FieldDefinition: () = {
<start:@L>
<description:Description?>
<name_start:@L>
<name:Name>
<name_end:@R>
<arguments:ArgumentsDefinition?> ":" <ty:Type>
<directives:Directives>
<end:@R>
=> {
let arguments = ast.input_value_definition_range(
Some(arguments.map(|arguments| arguments.len()).unwrap_or_default())
);
ast.field_definition(FieldDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
ty,
arguments,
description,
directives,
span: Span::new(start,end)
});
}
};
ArgumentsDefinition: Vec<()> = {
"(" <arguments:InputValueDefinition+> ")" => arguments,
};
InterfaceDefinition: InterfaceDefinitionRecord = {
<start:@L> interface
<name_start:@L>
<name:Name>
<name_end:@R>
<implements:ImplementsInterfaces?>
<directives:Directives>
<fields:FieldsDefinition?>
<end:@R>
=> InterfaceDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
directives,
implements_interfaces: implements.unwrap_or_default(),
fields: ast.field_definition_range(Some(fields.map(|fields| fields.len()).unwrap_or_default())),
span: Span::new(start,end)
}
};
UnionDefinition: UnionDefinitionRecord = {
<start:@L> union
<name_start:@L>
<name:Name>
<name_end:@R>
<directives:Directives>
<members:UnionMemberTypes?>
<end:@R>
=> UnionDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
members: ast.union_member_range(members.map(|members| members.len())),
directives,
span: Span::new(start,end)
}
};
UnionMemberTypes: Vec<UnionMemberId> = {
<members:UnionMemberTypes> "|" <start:@L> <name:NamedType> <end:@R> => {
let mut members = members;
members.push(ast.union_member(UnionMemberRecord { name, span: Span::new(start, end) }));
members
},
"=" "|"? <start:@L> <name:NamedType> <end:@R> => {
vec![ast.union_member(UnionMemberRecord { name, span: Span::new(start, end) })]
}
}
EnumDefinition: EnumDefinitionRecord = {
<start:@L> "enum"
<name_start:@L>
<name:Name>
<name_end:@R>
<directives:Directives>
<values:EnumValuesDefinition?>
<end:@R>
=> EnumDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
directives,
values: ast.enum_value_definition_range(values.map(|values| values.len())),
span: Span::new(start,end)
}
};
EnumValuesDefinition: Vec<EnumValueDefinitionId> = {
"{" <values:EnumValueDefinition+> "}" => values
}
EnumValueDefinition: EnumValueDefinitionId = {
<start:@L>
<description:Description?>
<value_start:@L>
<value:EnumValue>
<value_end:@R>
<directives:Directives>
<end:@R>
=> ast.enum_value_definition(EnumValueDefinitionRecord {
value,
value_span: Span::new(value_start, value_end),
description,
directives,
span: Span::new(start,end)
})
}
InputObjectDefinition: InputObjectDefinitionRecord = {
<start:@L> input
<name_start:@L>
<name:Name>
<name_end:@R>
<directives:Directives>
<fields:InputFieldsDefinition?>
<end:@R>
=> InputObjectDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
directives,
fields: ast.input_value_definition_range(Some(fields.map(|fields| fields.len()).unwrap_or_default())),
span: Span::new(start,end)
}
}
InputFieldsDefinition: Vec<()> = {
"{" <fields:InputValueDefinition+> "}" => fields
};
DirectiveDefinition: DirectiveDefinitionRecord = {
<start:@L>
directive
<name_start:@L>
"@" <name:Name>
<name_end:@R>
<arguments:ArgumentsDefinition?>
<repeatable:Repeatable?>
on
<locations:DirectiveLocations>
<end:@R>
=> DirectiveDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
description: None,
arguments: ast.input_value_definition_range(Some(arguments.map(|arguments| arguments.len()).unwrap_or_default())),
is_repeatable: repeatable.unwrap_or_default(),
locations,
span: Span::new(start, end)
}
}
Repeatable: bool = {
repeatable => true
}
DirectiveLocations: Vec<DirectiveLocation> = {
<locations:DirectiveLocations> "|" <location:DirectiveLocation> => {
let mut locations = locations;
locations.push(location);
locations
},
"|"? <location:DirectiveLocation> => {
vec![location]
}
}
DirectiveLocation: DirectiveLocation = {
<start:@L> <s:Ident> <end:@R> =>? {
s.parse::<DirectiveLocation>()
.map_err(|error| error.into_lalrpop_error((start, end)))
}
}
InputValueDefinition: () = {
<start:@L>
<description:Description?>
<name_start:@L>
<name:Name>
<name_end:@R>
":" <ty:Type>
<default_start:@L>
<default:DefaultValue?>
<default_end:@R>
<directives:Directives>
<end:@R>
=> {
ast.input_value_definition(InputValueDefinitionRecord {
name,
name_span: Span::new(name_start, name_end),
ty,
description,
directives,
default_value: default,
default_value_span: Span::new(default_start, default_end),
span: Span::new(start, end)
});
}
}
DefaultValue: ConstValueId = {
"=" <v:ConstValue> => v
}
Name: StringId = <s:Ident> => ast.ident(s);
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
}
ConstValue: ConstValueId = {
<record:ValueRecord> => {
ast.values.const_value(record)
}
}
Value: ValueId = {
<record:ValueRecord> => {
ast.values.value(record)
}
}
ValueRecord: ValueRecord = {
<start:@L> "$" <name:Ident> <end:@R> =>? {
Err(lalrpop_util::ParseError::User {
error: AdditionalErrors::VariableInConstPosition(start, name.to_string(), end)
})
},
<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_type_system_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_type_system_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> "[" <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)
}
},
<start:@L> <value:EnumValue> <end:@R> => {
ValueRecord {
span: Span::new(start, end),
kind: ValueKind::Enum(values::ids::StringId::from_type_system_id(value))
}
},
}
ObjectField: (values::ids::StringId, Span, ValueId) = {
<name_start:@L> <name:Name> <name_end:@R> ":" <value:Value> => {
(
values::ids::StringId::from_type_system_id(name),
Span::new(name_start, name_end),
value
)
}
}
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)
},
}
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"),
}
Directives: IdRange<DirectiveId> = {
<directives:Directive*> => {
ast.directive_range(Some(directives.len()))
}
}
Directive: () = {
<name_start:@L>
"@" <name:Name>
<name_end:@R>
<arguments_start:@L>
<arguments:Arguments?>
<arguments_end:@R>
=> {
let arguments = ast.argument_range(arguments.map(|arguments| arguments.len()));
ast.directive(DirectiveRecord {
name,
name_span: Span::new(name_start, name_end),
arguments,
arguments_span: Span::new(arguments_start, arguments_end)
});
}
}
Arguments: Vec<ArgumentId> = {
"(" <arguments:Argument*> ")" => arguments
}
Argument: ArgumentId = {
<start:@L>
<name:Name>
<name_end:@R>
":" <value:ConstValue>
<end:@R>
=> {
ast.argument(ArgumentRecord {
name,
name_span: Span::new(start, name_end),
value,
span: Span::new(start, end)
})
}
}
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,
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,
}
}