use lex_core::{parse_with_options, NumberFormatBuilder};
use nom::branch::alt;
use nom::bytes::complete::{escaped, tag, tag_no_case, take_until, take_while};
use nom::character::complete::{alpha1, alphanumeric1, char, multispace1, none_of, one_of};
use nom::combinator::{cut, map, map_res, opt, recognize, value};
use nom::error::{context, FromExternalError};
use nom::multi::{many0, many0_count, many1, separated_list1};
use nom::sequence::{delimited, pair, preceded, tuple};
use nom::Slice;
use nom_supreme::error::GenericErrorTree;
use nom_supreme::tag::TagError;
use protokit_desc::{BuiltinType, FieldNum, Frequency, ImportType};
use crate::ast::*;
use crate::deps::*;
use crate::{IResult, MyParseError, Parse};
pub const TAG_MAX: FieldNum = 536_870_911;
fn is_eol(c: char) -> bool {
c == '\r' || c == '\n'
}
fn semicolon(i: Span) -> IResult<()> {
match ws(tag(";"))(i) {
Ok((i, _)) => Ok((i, ())),
Err(_e) => IResult::Err(nom::Err::Failure(MyParseError::from_tag(i, "Trailing semicolon"))),
}
}
pub fn eol_comment(i: Span) -> IResult<Span> {
let (i, _) = tag("//")(i)?;
let (i, inside) = take_while(|c| !is_eol(c))(i)?;
let (i, _) = take_while(is_eol)(i)?;
Ok((i, inside))
}
pub fn inline_comment(i: Span) -> IResult<Span> {
let (i, _) = tag("/*")(i)?;
let (i, inside) = take_until("*/")(i)?;
let (i, _) = tag("*/")(i)?;
Ok((i, inside))
}
pub fn strdec(i: Span) -> Result<i128, lex_core::Error> {
parse_with_options::<_, { NumberFormatBuilder::decimal() }>(
i.as_bytes(),
&lex_core::parse_integer_options::STANDARD,
)
}
pub fn stroct(i: Span) -> Result<i128, lex_core::Error> {
parse_with_options::<_, { NumberFormatBuilder::octal() }>(i.as_bytes(), &lex_core::parse_integer_options::STANDARD)
}
pub fn strhex(i: Span) -> Result<i128, lex_core::Error> {
parse_with_options::<_, { NumberFormatBuilder::hexadecimal() }>(
i.as_bytes(),
&lex_core::parse_integer_options::STANDARD,
)
}
fn ws<'a, F: 'a, O>(mut inner: F) -> impl FnMut(Span<'a>) -> IResult<O>
where
F: FnMut(Span<'a>) -> IResult<O>,
{
move |i: Span| {
let (i, _) = many0(alt((eol_comment, inline_comment, multispace1)))(i)?;
inner(i)
}
}
fn determined<'i, I, O, II, OI>(mut first: I, mut second: O) -> impl FnMut(Span<'i>) -> IResult<OI>
where
I: FnMut(Span<'i>) -> IResult<II>,
O: FnMut(Span<'i>) -> IResult<OI>,
{
move |mut i| {
i = first(i)?.0;
cut(&mut second)(i)
}
}
fn prefixed<'i, P, R>(s: &'static str, parser: P) -> impl FnMut(Span<'i>) -> IResult<R>
where
P: FnMut(Span<'i>) -> IResult<R>,
{
determined(ws(tag(s)), context(s, parser))
}
fn ident(i: Span) -> IResult<Span> {
recognize(pair(
alt((alpha1, tag("_"))),
many0_count(alt((alphanumeric1, tag("_")))),
))(i)
}
fn full_ident(i: Span) -> IResult<Span> {
recognize(tuple((opt(char('.')), separated_list1(tag("."), ident))))(i)
}
fn msg_or_enum_type(i: Span) -> IResult<Type> {
map(recognize(tuple((opt(char('.')), full_ident))), |v| Type::Named(*v))(i)
}
fn oct_digit(i: Span) -> IResult<char> {
one_of("01234567")(i)
}
fn hex_digit(i: Span) -> IResult<char> {
one_of("0123456789abcdefABCDEF")(i)
}
fn dec_digit(i: Span) -> IResult<char> {
one_of("0123456789")(i)
}
fn hex_lit(i: Span) -> IResult<Span> {
preceded(alt((tag("0x"), tag("0X"))), recognize(many1(hex_digit)))(i)
}
fn octal_lit(i: Span) -> IResult<Span> {
preceded(alt((tag("0"), tag("0"))), recognize(many1(oct_digit)))(i)
}
fn decimal_lit(i: Span) -> IResult<Span> {
recognize(many1(dec_digit))(i)
}
fn int_lit(i: Span) -> IResult<i128> {
alt((
map_res(hex_lit, strhex),
map_res(octal_lit, stroct),
map_res(decimal_lit, strdec),
))(i)
}
fn field_num(i: Span) -> IResult<FieldNum> {
map(int_lit, |f| f.try_into().expect("Field number too big {}"))(i)
}
fn exponent(i: Span) -> IResult<Span> {
recognize(tuple((one_of("eE"), opt(one_of("+-")), decimal_lit)))(i)
}
fn float_lit(i: Span) -> IResult<f64> {
let a = recognize(tuple((decimal_lit, tag("."), opt(exponent), decimal_lit)));
let b = recognize(tuple((decimal_lit, exponent)));
let c = recognize(tuple((tag("."), decimal_lit, opt(exponent))));
let d = recognize(alt((tag("inf"), tag("nan"))));
map_res(alt((a, b, c, d)), |v| v.parse())(i)
}
fn bool_lit(i: Span) -> IResult<bool> {
map(alt((tag_no_case("true"), tag_no_case("false"))), |v: Span| *v == "true")(i)
}
fn escape_contents(i: Span) -> IResult<Span> {
let char_escape = recognize(one_of("abfnrtv\\'\"0?"));
let oct_escape = recognize(tuple((oct_digit, oct_digit, oct_digit)));
let hex_escape = recognize(tuple((one_of("xX"), hex_digit, hex_digit)));
alt((oct_escape, hex_escape, char_escape))(i)
}
fn str_lit(i: Span) -> IResult<Span> {
let normal = none_of("\\\0\n\"");
let contents = recognize(opt(escaped(normal, '\\', escape_contents)));
delimited(char('"'), contents, char('"'))(i)
}
fn opt_sign(i: Span) -> IResult<Option<char>> {
opt(one_of("+-"))(i)
}
fn compound_constant(_i: Span) -> IResult<Const<'_>> {
unimplemented!()
}
fn constant(i: Span) -> IResult<Const<'_>> {
let ilit = map(
tuple((opt_sign, int_lit)),
|(sign, val)| {
if sign == Some('-') {
-val
} else {
val
}
},
);
let flit = map(
tuple((opt_sign, float_lit)),
|(sign, val)| {
if sign == Some('-') {
-val
} else {
val
}
},
);
alt((
map(str_lit, |v| Const::Str(*v)),
map(bool_lit, Const::Bool),
map(recognize(full_ident), |v| Const::Ident(*v)),
map(flit, Const::Float),
map(ilit, Const::Int),
compound_constant,
))(i)
}
fn syntax(i: Span) -> IResult<Syntax> {
prefixed("syntax", |i| {
let (i, _) = ws(tag("="))(i)?;
let (i, syntax) = ws(map_res(alt((tag("\"proto2\""), tag("\"proto3\""))), |s: Span| {
Syntax::from_str(s.trim_matches('\"'))
}))(i)?;
let (i, _) = ws(tag(";"))(i)?;
Ok((i, syntax))
})(i)
}
fn import_type(i: Span) -> IResult<ImportType> {
map(
opt(alt((
value(ImportType::Weak, tag("weak")),
value(ImportType::Public, tag("public")),
))),
|v| v.unwrap_or(ImportType::Normal),
)(i)
}
fn import(i: Span) -> IResult<Import<'_>> {
prefixed("import", |i| {
let (i, typ) = ws(import_type)(i)?;
let (i, path) = ws(str_lit)(i)?;
let (i, _) = semicolon(i)?;
Ok((i, Import { typ, path }))
})(i)
}
fn package(i: Span) -> IResult<Package<'_>> {
prefixed("package", |i| {
let (i, path) = ws(recognize(full_ident))(i)?;
let (i, _) = semicolon(i)?;
Ok((i, Package { path }))
})(i)
}
fn option_name(i: Span) -> IResult<OptName> {
let simple = full_ident;
let complex_paren = delimited(tag("("), full_ident, tag(")"));
let complex_field = opt(preceded(ws(tag(".")), ws(ident)));
alt((
map(simple, |s| OptName {
name: s,
field_name: None,
}),
map(tuple((complex_paren, complex_field)), |(p, f)| OptName {
name: p,
field_name: f,
}),
))(i)
}
fn option(i: Span) -> IResult<super::ast::Opt<'_>> {
let (i, _) = ws(tag("option"))(i)?;
let (i, name) = ws(option_name)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, value) = ws(constant)(i)?;
let (i, _) = semicolon(i)?;
Ok((i, super::ast::Opt { name, value }))
}
fn builtin(i: Span) -> IResult<BuiltinType> {
let types = [
"double", "float", "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32",
"sfixed64", "bool", "string", "bytes",
];
for t in types {
if i.len() >= t.len() && i.starts_with(t) {
return Ok((i.slice(t.len() ..), BuiltinType::from_str(&i[.. t.len()]).unwrap()));
}
}
Err(nom::Err::Error(GenericErrorTree::from_external_error(
i,
nom::error::ErrorKind::Alpha,
"builtin type",
)))
}
fn ftype(i: Span) -> IResult<Type> {
alt((map(ws(builtin), Type::Builtin), ws(msg_or_enum_type)))(i)
}
fn field_option(i: Span) -> IResult<Opt<'_>> {
let (i, name) = ws(option_name)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, value) = ws(constant)(i)?;
Ok((i, Opt { name, value }))
}
fn field_options_brackets(i: Span) -> IResult<Vec<Opt<'_>>> {
let opts = separated_list1(tag(","), field_option);
delimited(ws(char('[')), opts, ws(char(']')))(i)
}
fn frequency(i: Span) -> IResult<Frequency> {
let (i, freq) = opt(alt((ws(tag("optional")), ws(tag("repeated")), ws(tag("required")))))(i)?;
let freq = match freq.map(|v| *v) {
Some("optional") => Frequency::Optional,
Some("repeated") => Frequency::Repeated,
Some("required") => Frequency::Required,
Some(_) => panic!("Unexpected frequency"),
None => Frequency::Singular,
};
Ok((i, freq))
}
fn field(i: Span) -> IResult<Field<'_>> {
let (i, frequency) = ws(frequency)(i)?;
let (i, ftype) = ws(ftype)(i)?;
let (i, name) = ws(ident)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, num) = ws(field_num)(i)?;
let (i, options) = ws(opt(field_options_brackets))(i)?;
let (i, _) = semicolon(i)?;
Ok((
i,
Field {
frequency,
typ: ftype,
name,
number: num,
opts: options.unwrap_or_default(),
},
))
}
fn oneof_field(i: Span) -> IResult<Field<'_>> {
let (i, ftype) = ws(ftype)(i)?;
let (i, name) = ws(ident)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, num) = ws(field_num)(i)?;
let (i, options) = ws(opt(field_options_brackets))(i)?;
let (i, _) = semicolon(i)?;
Ok((
i,
Field {
frequency: Frequency::Singular,
typ: ftype,
name,
number: num,
opts: options.unwrap_or_default(),
},
))
}
fn oneof_item(i: Span) -> IResult<OneOfItem<'_>> {
alt((
ws(map(option, OneOfItem::Option)),
ws(map(group, OneOfItem::Group)),
ws(map(oneof_field, OneOfItem::Field)),
))(i)
}
fn oneof_body(i: Span) -> IResult<Vec<OneOfItem<'_>>> {
delimited(ws(tag("{")), many0(ws(oneof_item)), ws(tag("}")))(i)
}
fn oneof(i: Span) -> IResult<OneOf<'_>> {
prefixed("oneof", |i| {
let (i, name) = ws(ident)(i)?;
let (i, items) = oneof_body(i)?;
Ok((i, OneOf { name, items }))
})(i)
}
fn key_type(i: Span) -> IResult<BuiltinType> {
builtin(i)
}
fn map_field(i: Span) -> IResult<MapField<'_>> {
prefixed("map", |i| {
let (i, _) = ws(tag("<"))(i)?;
let (i, key_type) = ws(key_type)(i)?;
let (i, _) = ws(tag(","))(i)?;
let (i, val_type) = ws(ftype)(i)?;
let (i, _) = ws(tag(">"))(i)?;
let (i, name) = ws(ident)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, number) = ws(field_num)(i)?;
let (i, options) = ws(opt(field_options_brackets))(i)?;
let (i, _) = semicolon(i)?;
Ok((
i,
MapField {
key_type,
val_type,
name,
number,
options: options.unwrap_or_default(),
},
))
})(i)
}
fn res_range(i: Span) -> IResult<ReservedRange> {
let (i, from) = ws(field_num)(i)?;
let max = ws(value(TAG_MAX, tag("max")));
let end = ws(alt((field_num, max)));
let to = opt(preceded(ws(tag("to")), end));
let (i, to) = ws(to)(i)?;
Ok((
i,
ReservedRange {
from,
to: to.unwrap_or(from),
},
))
}
fn res_str_item(i: Span) -> IResult<Span> {
alt((
delimited(char('\''), ident, char('\'')),
delimited(char('\"'), ident, char('\"')),
))(i)
}
fn reserved(i: Span) -> IResult<Reserved<'_>> {
prefixed("reserved", |i| {
let ranges = separated_list1(ws(char(',')), ws(res_range));
let str_names = separated_list1(ws(char(',')), ws(res_str_item));
let (i, items) = ws(alt((map(str_names, Reserved::Names), map(ranges, Reserved::Ranges))))(i)?;
let (i, _) = ws(tag(";"))(i)?;
Ok((i, items))
})(i)
}
fn extensions(i: Span) -> IResult<Extensions> {
prefixed("extensions", |i| {
let ranges = separated_list1(ws(char(',')), ws(res_range));
let (i, ranges) = ws(ranges)(i)?;
let (i, _) = ws(tag(";"))(i)?;
Ok((i, Extensions { ranges }))
})(i)
}
fn enum_lit(i: Span) -> IResult<i32> {
let (i, (sign, mut val)) = tuple((opt(char('-')), int_lit))(i)?;
if let Some('-') = sign {
val = -val;
};
Ok((i, val.try_into().expect("Field number too big")))
}
fn enum_field(i: Span) -> IResult<EnumField<'_>> {
let (i, name) = ws(ident)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, value) = ws(enum_lit)(i)?;
let (i, options) = ws(opt(field_options_brackets))(i)?;
let (i, _) = ws(tag(";"))(i)?;
Ok((
i,
EnumField {
name,
value,
opts: options.unwrap_or_default(),
},
))
}
fn enum_item(i: Span) -> IResult<EnumItem<'_>> {
alt((map(option, EnumItem::Option), map(enum_field, EnumItem::Field)))(i)
}
fn enum_body(i: Span) -> IResult<Vec<EnumItem<'_>>> {
let (i, body) = delimited(ws(tag("{")), many0(ws(enum_item)), ws(tag("}")))(i)?;
let (i, _) = ws(opt(char(';')))(i)?;
Ok((i, body))
}
fn _enum(i: Span) -> IResult<Enum> {
prefixed("enum", |i| {
let (i, name) = ws(ident)(i)?;
let (i, items) = ws(enum_body)(i)?;
Ok((i, Enum { name, items }))
})(i)
}
fn message_item(i: Span) -> IResult<MessageItem<'_>> {
alt((
map(group, MessageItem::Group),
map(field, MessageItem::Field),
map(_enum, MessageItem::Enum),
map(message, MessageItem::Message),
map(option, MessageItem::Option),
map(oneof, MessageItem::OneOf),
map(map_field, MessageItem::MapField),
map(reserved, MessageItem::Reserved),
map(extensions, MessageItem::Extensions),
map(extend, MessageItem::Extend),
))(i)
}
fn message_body(i: Span) -> IResult<Vec<MessageItem<'_>>> {
let (i, body) = delimited(ws(tag("{")), many0(ws(message_item)), ws(tag("}")))(i)?;
let (i, _) = ws(opt(char(';')))(i)?;
Ok((i, body))
}
fn message(i: Span) -> IResult<Message<'_>> {
determined(ws(tag("message")), |i| {
let (i, name) = ws(ident)(i)?;
let (i, items) = ws(message_body)(i)?;
Ok((i, Message { name, items }))
})(i)
}
fn rpc_arg(i: Span) -> IResult<(bool, Type)> {
delimited(
ws(tag("(")),
tuple((ws(map(opt(tag("stream")), |v| v.is_some())), ws(msg_or_enum_type))),
ws(tag(")")),
)(i)
}
fn rpc_options(i: Span) -> IResult<Vec<Opt<'_>>> {
delimited(ws(char('{')), many0(option), ws(tag("}")))(i)
}
fn rpc(i: Span) -> IResult<Rpc> {
prefixed("rpc", |i| {
let _opts = alt((ws(rpc_options), ws(map(tag(";"), |_| vec![]))));
let (i, name) = ws(ident)(i)?;
let (i, arg) = ws(rpc_arg)(i)?;
let (i, _) = ws(tag("returns"))(i)?;
let (i, ret) = ws(rpc_arg)(i)?;
let (i, options) = ws(_opts)(i)?;
Ok((
i,
Rpc {
name,
msg_type: arg.1,
msg_stream: arg.0,
ret_type: ret.1,
ret_stream: ret.0,
options,
},
))
})(i)
}
fn service_body(i: Span) -> IResult<Vec<ServiceItem<'_>>> {
delimited(
ws(tag("{")),
many0(ws(alt((
map(option, ServiceItem::Option),
map(rpc, ServiceItem::Rpc),
)))),
ws(tag("}")),
)(i)
}
fn service(i: Span) -> IResult<Service<'_>> {
prefixed("service", |i| {
let (i, name) = ws(ident)(i)?;
let (i, items) = ws(service_body)(i)?;
Ok((i, Service { name, items }))
})(i)
}
fn extend_item(i: Span) -> IResult<ExtensionItem> {
alt((map(field, ExtensionItem::Field), map(group, ExtensionItem::Group)))(i)
}
fn extend_body(i: Span) -> IResult<Vec<ExtensionItem>> {
delimited(ws(char('{')), ws(many0(extend_item)), ws(char('}')))(i)
}
fn extend(i: Span) -> IResult<Extension<'_>> {
prefixed("extend", |i| {
let (i, mtype) = ws(recognize(full_ident))(i)?;
let (i, items) = ws(extend_body)(i)?;
Ok((i, Extension { name: mtype, items }))
})(i)
}
fn group(i: Span) -> IResult<Group> {
let (i, freq) = ws(frequency)(i)?;
prefixed("group", move |i| {
let (i, name) = ws(ident)(i)?;
let (i, _) = ws(tag("="))(i)?;
let (i, number) = ws(field_num)(i)?;
let (i, items) = ws(message_body)(i)?;
Ok((
i,
Group {
frequency: freq,
name,
number,
items,
},
))
})(i)
}
fn def(i: Span) -> IResult<Def<'_>> {
alt((
map(message, Def::Message),
map(_enum, Def::Enum),
map(service, Def::Service),
map(extend, Def::Extend),
))(i)
}
fn file_item(i: Span) -> IResult<ProtoItem> {
alt((
map(import, ProtoItem::Import),
map(package, ProtoItem::Package),
map(option, ProtoItem::Option),
map(def, ProtoItem::Def),
))(i)
}
impl<'i> Parse<'i> for Proto<'i> {
fn parse(i: Span<'i>) -> IResult<'i, Self> {
let (i, syntax) = syntax(i)?;
let (i, items) = many0(file_item)(i)?;
let (i, _) = ws(tag(""))(i)?;
Ok((i, Proto { syntax, items }))
}
}
#[test]
fn test_proto_file() {
let input = r#"
syntax = "proto3";
message Outer {
Strict ival = 11;
}"#;
match Proto::parse_format_error(input) {
Ok(_) => {}
Err(e) => {
let mut s = String::new();
miette::GraphicalReportHandler::new().render_report(&mut s, &e).unwrap();
println!("{s}");
}
};
}