// vim: ft=rust
use core::{RpNumber, Loc, RpPackage, Source, Span};
use std::sync::Arc;
use ast::*;
use lexer::{self, Token};
use super::utils;
use std::borrow::Cow;
grammar<'input>;
pub File: File<'input> =
<comment:"//!"?>
<attributes:Loc<FileAttribute>*>
<uses:Loc<Use>*>
<decls:Decl*> => {
File {
comment: comment.unwrap_or_else(Vec::new),
attributes: attributes,
uses: uses,
decls: decls,
}
};
Use: UseDecl<'input> = {
"use" <package:Loc<Package>>
<range:Loc<"string">?>
<alias:UseAlias?>
<endl:(@L ";" @R)?> => UseDecl {
package: package,
range: range,
alias: alias,
endl: endl.map(|(s, _, e)| Span::from((s, e))),
},
};
UseAlias: Loc<Cow<'input, str>> = "as" <value:Loc<ident>> => value;
Package: Package<'input> = {
<parts:OneOrMore<".", Loc<Ident>>> => Package::Package { parts },
! => Package::Error,
};
Decl: Decl<'input> = {
<en:Item<EnumBody>> => Decl::Enum(en),
<interface:Item<InterfaceBody>> => Decl::Interface(interface),
<ty:Item<TypeBody>> => Decl::Type(ty),
<tuple:Item<TupleBody>> => Decl::Tuple(tuple),
<service:Item<ServiceBody>> => Decl::Service(service),
};
EnumBody: EnumBody<'input> =
"enum" <name:Loc<TypeIdent>> "as" <ty:Loc<Type>> "{"
<variants:Item<EnumVariant>*>
<members:EnumMember*>
"}" =>
EnumBody {
name: name,
ty: ty,
variants: variants,
members: members,
};
EnumMember: EnumMember<'input> = {
<code:Loc<Code>> => EnumMember::Code(code),
};
InterfaceBody: InterfaceBody<'input> =
"interface" <name:Loc<TypeIdent>> "{"
<members:TypeMember*>
<sub_types:Item<SubType>*>
"}" =>
InterfaceBody {
name: name,
members: members,
sub_types: sub_types,
};
TypeBody: TypeBody<'input> =
"type" <name:Loc<TypeIdent>> "{"
<members:TypeMember*>
"}" =>
TypeBody {
name: name,
members: members,
};
TupleBody: TupleBody<'input> =
"tuple" <name:Loc<TypeIdent>> "{"
<members:TypeMember*>
"}" =>
TupleBody {
name: name,
members: members,
};
ServiceBody: ServiceBody<'input> =
"service" <name:Loc<TypeIdent>> "{"
<members:ServiceMember*>
"}" =>
ServiceBody {
name: name,
members: members,
};
pub ServiceMember: ServiceMember<'input> = {
<endpoint:Item<Endpoint>> => {
ServiceMember::Endpoint(endpoint)
},
<decl:Decl> => {
ServiceMember::InnerDecl(decl)
},
};
Endpoint: Endpoint<'input> = {
<id:Loc<Ident>> "("
<arguments:ZeroOrMore<",", EndpointArgument>>
")" <response:("->" Loc<Channel>)?>
<alias:FieldAlias?>
";" => {
Endpoint {
id: id,
alias: alias,
arguments: arguments,
response: response.map(|r| r.1),
}
},
};
EndpointArgument: EndpointArgument<'input> =
<ident:Loc<Ident>> ":" <channel:Loc<Channel>> => EndpointArgument {
ident: ident,
channel: channel,
};
Channel: Channel<'input> = {
<stream:stream?> <ty:Loc<Type>> => {
if stream.is_some() {
Channel::Streaming {
ty: ty
}
} else {
Channel::Unary {
ty: ty
}
}
},
};
Ident: Cow<'input, str> = {
<value:ident> => value,
};
FieldAlias: String = {
"as" <value:Ident> => value.to_string(),
"as" <value:"string"> => value,
};
EnumVariant: EnumVariant<'input> =
<name:Loc<TypeIdent>> <argument:("as" Loc<Value>)?> ";" =>
EnumVariant {
name: name,
argument: argument.map(|a| a.1),
};
Optional: bool = <modifier:"?"?> => modifier.map(|_| true).unwrap_or(false);
pub TypeMember: TypeMember<'input> = {
<field:Item<Field>> => TypeMember::Field(field),
<code:Loc<Code>> => TypeMember::Code(code),
<decl:Decl> => TypeMember::InnerDecl(decl),
};
Code: Code<'input> =
<attributes:Loc<Attribute>*>
<context:Loc<Ident>> "{{" <content:code> "}}" =>
Code {
attributes: attributes,
context: context,
content: utils::strip_code_block(content),
};
Field: Field<'input> = {
<name:Ident> <optional:Optional> ":" <ty:Loc<Type>> <alias:FieldAlias?> <endl:";"?> =>
Field {
required: !optional,
name: name,
ty: ty,
field_as: alias,
endl: endl.is_some(),
},
};
SubType: SubType<'input> = {
<name:Loc<TypeIdent>> <alias:("as" Loc<Value>)?> "{"
<members:TypeMember*>
"}" =>
SubType {
name: name,
members: members,
alias: alias.map(|alias| alias.1),
},
<name:Loc<TypeIdent>> <alias:("as" Loc<Value>)?> ";" =>
SubType {
name: name,
members: Vec::new(),
alias: alias.map(|alias| alias.1),
},
};
pub Value: Value<'input> = {
"(" <values:ZeroOrMore<",", Loc<Value>>> ")" => Value::Array(values),
<string:"string"> => Value::String(string),
<number:number> => Value::Number(number),
<id:Ident> => Value::Identifier(id),
};
pub Type: Type<'input> = {
any => Type::Any,
float => Type::Float,
double => Type::Double,
i32 => Type::Signed { size: 32 },
i64 => Type::Signed { size: 64 },
u32 => Type::Unsigned { size: 32 },
u64 => Type::Unsigned { size: 64 },
boolean => Type::Boolean,
string => Type::String,
datetime => Type::DateTime,
bytes => Type::Bytes,
<name:Loc<Name>> => Type::Name {
name
},
"[" <inner:Loc<Type>> "]" => Type::Array {
inner: Box::new(inner)
},
"{" <key:Loc<Type>> ":" <value:Loc<Type>> "}" => Type::Map {
key: Box::new(key),
value: Box::new(value)
},
<e:!> => Type::Error,
};
Name: Name<'input> = {
<prefix:(Loc<ident> "::")?> <parts:OneOrMore<"::", Loc<TypeIdent>>> => Name::Absolute {
prefix: prefix.map(|p| p.0),
parts,
},
"::" <parts:OneOrMore<"::", Loc<TypeIdent>>> => Name::Relative {
parts,
},
};
FileAttribute: Attribute<'input> = {
"#" "!" "[" <id:Loc<Ident>> "]" => {
Attribute::Word(id)
},
"#" "!" "[" <id:Loc<Ident>> "(" <name_value:ZeroOrMoreTrailing<",", AttributeItem>> ")" "]" => {
Attribute::List(id, name_value)
},
};
Attribute: Attribute<'input> = {
"#" "[" <id:Loc<Ident>> "]" => {
Attribute::Word(id)
},
"#" "[" <id:Loc<Ident>> "(" <name_value:ZeroOrMoreTrailing<",", AttributeItem>> ")" "]" => {
Attribute::List(id, name_value)
},
};
AttributeItem: AttributeItem<'input> = {
<id:Loc<Ident>> "=" <value:Loc<Value>> => AttributeItem::NameValue {
name: id,
value: value,
},
<value:Loc<Value>> => {
AttributeItem::Word(value)
},
};
/// Zero or more matching, separated by a token.
ZeroOrMore<Sep, T>: Vec<T> =
<value:OneOrMore<Sep, T>?> =>
value.unwrap_or_else(|| vec![]);
/// One or more matching, separated by a token.
OneOrMore<Sep, T>: Vec<T> =
<values:(<T> Sep)*> <last:T> => {
let mut values = values;
values.push(last);
values
};
ZeroOrMoreTrailing<Sep, T>: Vec<T> = {
<values:(<T> Sep)*> <last:T> Sep? => {
let mut values = values;
values.push(last);
values
},
() => vec![],
};
Loc<Inner>: Loc<Inner> = <s:@L> <inner:Inner> <e:@R> =>
Loc::new(inner, (s, e));
Item<T>: Item<'input, T> =
<comment:"///"?>
<attributes:Loc<Attribute>*>
<s:@L> <item:T> <e:@R> =>
Item {
comment: comment.unwrap_or_else(Vec::new),
attributes: attributes,
item: Loc::new(item, (s, e))
};
extern {
type Location = usize;
type Error = lexer::Error;
enum Token<'input> {
TypeIdent => Token::TypeIdentifier(<Cow<'input, str>>),
ident => Token::Identifier(<Cow<'input, str>>),
number => Token::Number(<RpNumber>),
"use" => Token::Use,
"as" => Token::As,
"enum" => Token::Enum,
"type" => Token::Type,
"interface" => Token::Interface,
"tuple" => Token::Tuple,
"service" => Token::Service,
"(" => Token::LeftParen,
")" => Token::RightParen,
"{" => Token::LeftCurly,
"}" => Token::RightCurly,
"{{" => Token::CodeOpen,
"}}" => Token::CodeClose,
code => Token::CodeContent(<Cow<'input, str>>),
"string" => Token::QuotedString(<String>),
"[" => Token::LeftBracket,
"]" => Token::RightBracket,
";" => Token::SemiColon,
":" => Token::Colon,
"?" => Token::QuestionMark,
"#" => Token::Hash,
"!" => Token::Bang,
"->" => Token::RightArrow,
"," => Token::Comma,
"." => Token::Dot,
"::" => Token::Scope,
"=" => Token::Equal,
"//!" => Token::PackageDocComment(<Vec<Cow<'input, str>>>),
"///" => Token::DocComment(<Vec<Cow<'input, str>>>),
any => Token::Any,
float => Token::Float,
double => Token::Double,
u32 => Token::U32,
u64 => Token::U64,
i32 => Token::I32,
i64 => Token::I64,
boolean => Token::Boolean,
string => Token::String,
datetime => Token::Datetime,
bytes => Token::Bytes,
stream => Token::Stream,
}
}