1use nom::{
2 branch::alt,
3 bytes::complete::{tag, take_while1},
4 character::complete::{char, multispace0},
5 combinator::{cut, map, opt},
6 error::{context, ParseError},
7 multi::separated_list0,
8 sequence::{preceded, separated_pair, terminated},
9 IResult,
10};
11
12use crate::ast::{
13 Enum, EnumVariant, NamedField, PrimitiveType, QualifiedIdentifier, Struct, Type, TypeBody,
14 TypeDef,
15};
16
17fn identifier(i: &str) -> IResult<&str, &str> {
18 take_while1(|c: char| c.is_alphanumeric() || c == '_')(i)
19}
20
21fn qualified_identifier(i: &str) -> IResult<&str, QualifiedIdentifier> {
22 let (i, maybe_module): (_, Option<&str>) = opt(|i| {
23 let (i, module) = identifier(i)?;
24 let (i, _) = char('.')(i)?;
25 Ok((i, module))
26 })(i)?;
27
28 let (i, name) = identifier(i)?;
29
30 Ok((
31 i,
32 QualifiedIdentifier {
33 name: name.to_string(),
34 module: maybe_module.map(|x| x.to_string()),
35 },
36 ))
37}
38
39fn opt_trailing_comma<I, O, E: ParseError<I>>(
40 f: impl FnMut(I) -> IResult<I, O, E>,
41) -> impl FnMut(I) -> IResult<I, O, E>
42where
43 I: nom::Slice<std::ops::RangeFrom<usize>> + nom::InputIter + Clone,
44 <I as nom::InputIter>::Item: nom::AsChar,
45{
46 terminated(f, opt(char(',')))
47}
48
49fn box_ty(i: &str) -> IResult<&str, PrimitiveType> {
50 let (i, _) = tag("box")(i)?;
51 let (i, _) = multispace0(i)?;
52 let (i, _) = tag("<")(i)?;
53 let (i, _) = multispace0(i)?;
54 let (i, inner_ty) = ty(i)?;
55 let (i, _) = multispace0(i)?;
56 let (i, _) = tag(">")(i)?;
57
58 Ok((i, PrimitiveType::Box(inner_ty.into())))
59}
60
61fn list_ty(i: &str) -> IResult<&str, PrimitiveType> {
62 let (i, _) = char('[')(i)?;
63 let (i, _) = multispace0(i)?;
64 let (i, ty) = ty(i)?;
65 let (i, _) = multispace0(i)?;
66 let (i, _) = char(']')(i)?;
67
68 Ok((i, PrimitiveType::List(ty.into())))
69}
70
71fn option_ty(i: &str) -> IResult<&str, PrimitiveType> {
72 let (i, _) = tag("option")(i)?;
73 let (i, _) = multispace0(i)?;
74 let (i, _) = tag("<")(i)?;
75 let (i, _) = multispace0(i)?;
76 let (i, ty) = ty(i)?;
77 let (i, _) = multispace0(i)?;
78 let (i, _) = tag(">")(i)?;
79
80 Ok((i, PrimitiveType::Option(ty.into())))
81}
82
83fn result_ty(i: &str) -> IResult<&str, PrimitiveType> {
84 let (i, _) = tag("result")(i)?;
85 let (i, _) = multispace0(i)?;
86 let (i, _) = tag("<")(i)?;
87 let (i, ok_ty) = ty(i)?;
88 let (i, _) = multispace0(i)?;
89 let (i, _) = char(',')(i)?;
90 let (i, _) = multispace0(i)?;
91 let (i, err_ty) = ty(i)?;
92 let (i, _) = multispace0(i)?;
93 let (i, _) = opt(char(','))(i)?;
94 let (i, _) = multispace0(i)?;
95 let (i, _) = tag(">")(i)?;
96
97 Ok((i, PrimitiveType::Result(ok_ty.into(), err_ty.into())))
98}
99
100fn builtin_ty(i: &str) -> IResult<&str, PrimitiveType> {
101 alt((
102 map(tag("void"), |_| PrimitiveType::Void),
103 map(tag("u8"), |_| PrimitiveType::U8),
104 map(tag("u16"), |_| PrimitiveType::U16),
105 map(tag("u32"), |_| PrimitiveType::U32),
106 map(tag("u64"), |_| PrimitiveType::U64),
107 map(tag("u128"), |_| PrimitiveType::U128),
108 map(tag("i8"), |_| PrimitiveType::I8),
109 map(tag("i16"), |_| PrimitiveType::I16),
110 map(tag("i32"), |_| PrimitiveType::I32),
111 map(tag("i64"), |_| PrimitiveType::I64),
112 map(tag("i128"), |_| PrimitiveType::I128),
113 map(tag("f32"), |_| PrimitiveType::F32),
114 map(tag("bool"), |_| PrimitiveType::Bool),
115 map(tag("string"), |_| PrimitiveType::String),
116 box_ty,
117 list_ty,
118 option_ty,
119 result_ty,
120 ))(i)
121}
122
123pub fn type_params_list(i: &str) -> IResult<&str, Vec<String>> {
124 let (i, _) = tag("<")(i)?;
125 let (i, params) = separated_list0(
126 preceded(multispace0, char(',')),
127 preceded(multispace0, identifier),
128 )(i)?;
129 let (i, _) = multispace0(i)?;
130 let (i, _) = opt(char(','))(i)?;
131 let (i, _) = multispace0(i)?;
132 let (i, _) = tag(">")(i)?;
133
134 let params = params.into_iter().map(|p| p.into()).collect();
135
136 Ok((i, params))
137}
138
139pub fn type_args_list(i: &str) -> IResult<&str, Vec<Type>> {
140 let (i, _) = tag("<")(i)?;
141 let (i, args) =
142 separated_list0(preceded(multispace0, char(',')), preceded(multispace0, ty))(i)?;
143 let (i, _) = multispace0(i)?;
144 let (i, _) = opt(char(','))(i)?;
145 let (i, _) = multispace0(i)?;
146 let (i, _) = tag(">")(i)?;
147
148 let args = args.into_iter().map(|p| p.into()).collect();
149
150 Ok((i, args))
151}
152
153pub fn struct_def(i: &str) -> IResult<&str, TypeDef> {
154 let (i, _) = tag("struct")(i)?;
155 let (i, _) = multispace0(i)?;
156 let (i, name) = identifier(i)?;
157 let (i, _) = multispace0(i)?;
158 let (i, maybe_params) = opt(type_params_list)(i)?;
159 let (i, _) = multispace0(i)?;
160 let (i, fields) = named_fields(i)?;
161
162 let params = maybe_params.unwrap_or(Vec::new());
163
164 let type_def = TypeDef {
165 name: name.into(),
166 params,
167 body: TypeBody::Struct(Struct { fields }),
168 };
169
170 Ok((i, type_def))
171}
172
173fn enum_def<'a>(i: &'a str) -> IResult<&'a str, TypeDef> {
174 let (i, _) = tag("enum")(i)?;
175 let (i, _) = multispace0(i)?;
176 let (i, name) = identifier(i)?;
177 let (i, _) = multispace0(i)?;
178 let (i, maybe_params) = opt(type_params_list)(i)?;
179 let (i, _) = multispace0(i)?;
180 let (i, variants) = enum_variants(i)?;
181
182 let params = maybe_params.unwrap_or(Vec::new());
183
184 let type_def = TypeDef {
185 name: name.into(),
186 params,
187 body: TypeBody::Enum(Enum { variants }),
188 };
189
190 Ok((i, type_def))
191}
192
193fn enum_variants<'a>(i: &'a str) -> IResult<&'a str, Vec<(String, EnumVariant)>> {
194 context(
195 "map",
196 preceded(
197 char('{'),
198 cut(terminated(
199 opt_trailing_comma(map(
200 separated_list0(
201 preceded(multispace0, char(',')),
202 preceded(multispace0, enum_variant),
203 ),
204 |tuple_vec| {
205 tuple_vec
206 .into_iter()
207 .map(|(name, variant)| (name.into(), variant))
208 .collect()
209 },
210 )),
211 preceded(multispace0, char('}')),
212 )),
213 ),
214 )(i)
215}
216
217fn enum_variant<'a>(i: &'a str) -> IResult<&'a str, (&'a str, EnumVariant)> {
218 alt((
219 separated_pair(
220 identifier,
221 multispace0,
222 map(named_fields, |fields| EnumVariant::NamedFields { fields }),
223 ),
224 map(identifier, |x| (x, EnumVariant::Empty)),
225 ))(i)
226}
227
228pub fn defined_ty<'a>(i: &'a str) -> IResult<&'a str, Type> {
229 let (i, ident) = qualified_identifier(i)?;
230 let (i, _) = multispace0(i)?;
231 let (i, maybe_args) = opt(type_args_list)(i)?;
232
233 let args = maybe_args.unwrap_or(Vec::new());
234 let defined_type = Type::Defined { ident, args };
235
236 Ok((i, defined_type))
237}
238
239pub fn ty<'a>(i: &'a str) -> IResult<&'a str, Type> {
240 alt((map(builtin_ty, |x| Type::Primitive(x)), defined_ty))(i)
241}
242
243pub fn type_def<'a>(i: &'a str) -> IResult<&'a str, TypeDef> {
244 alt((struct_def, enum_def))(i)
245}
246
247fn named_field<'a>(i: &'a str) -> IResult<&'a str, (&'a str, Type)> {
248 separated_pair(
249 identifier,
250 cut(preceded(multispace0, char(':'))),
251 preceded(multispace0, ty),
252 )(i)
253}
254
255fn named_fields<'a>(i: &'a str) -> IResult<&'a str, Vec<NamedField>> {
256 context(
257 "map",
258 preceded(
259 char('{'),
260 cut(terminated(
261 opt_trailing_comma(map(
262 separated_list0(
263 preceded(multispace0, char(',')),
264 preceded(multispace0, named_field),
265 ),
266 |tuple_vec| {
267 tuple_vec
268 .into_iter()
269 .map(|(k, v)| NamedField {
270 name: k.to_owned(),
271 ty: v,
272 })
273 .collect()
274 },
275 )),
276 preceded(multispace0, char('}')),
277 )),
278 ),
279 )(i)
280}
281
282pub fn root<'a>(i: &'a str) -> IResult<&'a str, Vec<TypeDef>> {
283 separated_list0(multispace0, type_def)(i)
284}
285
286pub fn parse_file(path: impl AsRef<std::path::Path>) -> Result<Vec<TypeDef>, String> {
287 use std::io::Read;
288
289 let Ok(mut file) = std::fs::File::open(path.as_ref()) else {
291 return Err(format!("Failed to open file '{}'", path.as_ref().display()));
292 };
293
294 let mut file_str = String::new();
296 if let Err(e) = file.read_to_string(&mut file_str) {
297 return Err(format!(
298 "Failed to read file '{}': {}",
299 path.as_ref().display(),
300 e
301 ));
302 }
303
304 let (_, type_defs) = root(&file_str).map_err(|e| format!("mproto schema parse error: {e}"))?;
305
306 Ok(type_defs)
307}
308
309#[cfg(test)]
310mod tests {
311 use super::*;
312
313 #[test]
314 fn test_builtin_u8() {
315 let data = "u8";
316 let (_, parsed) = builtin_ty(data).unwrap();
317
318 assert_eq!(parsed, PrimitiveType::U8);
319 }
320
321 #[test]
322 fn test_list_u8() {
323 let data = "[u8]";
324 let (_, parsed) = list_ty(data).unwrap();
325
326 assert_eq!(
327 parsed,
328 PrimitiveType::List(Box::new(Type::Primitive(PrimitiveType::U8))),
329 );
330 }
331
332 #[test]
333 fn test_box_u8() {
334 let data = "box<u8>";
335 let (_, parsed) = box_ty(data).unwrap();
336
337 assert_eq!(
338 parsed,
339 PrimitiveType::Box(Box::new(Type::Primitive(PrimitiveType::U8))),
340 );
341 }
342
343 #[test]
344 fn test_option_u8() {
345 let data = "option<u8>";
346 let (_, parsed) = option_ty(data).unwrap();
347
348 assert_eq!(
349 parsed,
350 PrimitiveType::Option(Box::new(Type::Primitive(PrimitiveType::U8))),
351 );
352 }
353
354 #[test]
355 fn test_result() {
356 let data = "result<void, string>";
357 let (_, parsed) = result_ty(data).unwrap();
358
359 assert_eq!(
360 parsed,
361 PrimitiveType::Result(
362 Box::new(Type::Primitive(PrimitiveType::Void)),
363 Box::new(Type::Primitive(PrimitiveType::String)),
364 ),
365 );
366 }
367
368 #[test]
369 fn test_struct_named_fields() {
370 use PrimitiveType::*;
371
372 let data = "struct Foo { bar : u32, baz : i8 }";
373 let (_, parsed) = struct_def(data).unwrap();
374
375 assert_eq!(
376 parsed,
377 TypeDef {
378 name: "Foo".into(),
379 params: vec![],
380 body: TypeBody::Struct(Struct {
381 fields: vec![
382 NamedField {
383 name: "bar".into(),
384 ty: Type::Primitive(U32)
385 },
386 NamedField {
387 name: "baz".into(),
388 ty: Type::Primitive(I8)
389 },
390 ]
391 }),
392 }
393 );
394 }
395
396 #[test]
397 fn test_struct_type_param() {
398 use PrimitiveType::*;
399
400 let data = "struct Foo <T, F> { bar : u32, baz : i8 }";
401 let (_, parsed) = struct_def(data).unwrap();
402
403 assert_eq!(
404 parsed,
405 TypeDef {
406 name: "Foo".into(),
407 params: vec!["T".into(), "F".into()],
408 body: TypeBody::Struct(Struct {
409 fields: vec![
410 NamedField {
411 name: "bar".into(),
412 ty: Type::Primitive(U32)
413 },
414 NamedField {
415 name: "baz".into(),
416 ty: Type::Primitive(I8)
417 },
418 ]
419 }),
420 }
421 );
422 }
423
424 #[test]
425 fn test_defined_type() {
426 use PrimitiveType::*;
427
428 let data = "struct Foo { bar : bar_proto.Bar, baz : i8 }";
429 let (_, parsed) = struct_def(data).unwrap();
430
431 assert_eq!(
432 parsed,
433 TypeDef {
434 name: "Foo".into(),
435 params: vec![],
436 body: TypeBody::Struct(Struct {
437 fields: vec![
438 NamedField {
439 name: "bar".into(),
440 ty: Type::Defined {
441 ident: QualifiedIdentifier {
442 module: Some("bar_proto".into()),
443 name: "Bar".into(),
444 },
445 args: vec![],
446 },
447 },
448 NamedField {
449 name: "baz".into(),
450 ty: Type::Primitive(I8)
451 },
452 ]
453 }),
454 }
455 );
456 }
457
458 #[test]
459 fn test_enum_named_fields() {
460 use PrimitiveType::*;
461
462 let data = "enum Foo { Bar { x: u32, y: u8 }, Baz { bip: i8 } }";
463 let (_, parsed) = enum_def(data).unwrap();
464
465 assert_eq!(
466 parsed,
467 TypeDef {
468 name: "Foo".into(),
469 params: vec![],
470 body: TypeBody::Enum(Enum {
471 variants: vec![
472 (
473 "Bar".into(),
474 EnumVariant::NamedFields {
475 fields: vec![
476 NamedField {
477 name: "x".into(),
478 ty: Type::Primitive(U32)
479 },
480 NamedField {
481 name: "y".into(),
482 ty: Type::Primitive(U8)
483 },
484 ],
485 }
486 ),
487 (
488 "Baz".into(),
489 EnumVariant::NamedFields {
490 fields: vec![NamedField {
491 name: "bip".into(),
492 ty: Type::Primitive(I8)
493 },],
494 }
495 ),
496 ]
497 }),
498 }
499 );
500 }
501}