reproto-parser 0.3.36

reproto parser
// 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,
    }
}