ruggle_engine/query/
parse.rs

1use nom::{
2    branch::alt,
3    bytes::complete::{tag, take_while1},
4    character::complete::char,
5    character::complete::{alpha1, alphanumeric1, multispace0, multispace1},
6    combinator::{eof, fail, map, not, opt, recognize, value},
7    error::{ContextError, ParseError},
8    multi::{many0, separated_list0},
9    sequence::{delimited, pair, preceded},
10    IResult,
11};
12
13use crate::query::*;
14
15type Symbol = String;
16
17pub fn parse_query(i: &str) -> IResult<&str, Query> {
18    parse_function_query(i)
19}
20
21fn parse_symbol<'a, E>(i: &'a str) -> IResult<&'a str, Symbol, E>
22where
23    E: ParseError<&'a str> + ContextError<&'a str>,
24{
25    map(
26        recognize(pair(
27            alt((tag("_"), alpha1)),
28            many0(alt((tag("_"), alphanumeric1))),
29        )),
30        |symbol: &str| symbol.to_string(),
31    )(i)
32}
33
34fn parse_function_query<'a, E>(i: &'a str) -> IResult<&'a str, Query, E>
35where
36    E: ParseError<&'a str> + ContextError<&'a str>,
37{
38    let (i, qualifiers) = opt(preceded(
39        multispace0,
40        many0(preceded(
41            multispace0,
42            alt((
43                tag("pub"),
44                tag("async"),
45                tag("unsafe"),
46                tag("extern"),
47                tag("const"),
48                tag("fn"),
49            )),
50        )),
51    ))(i)?;
52
53    let qualifiers = qualifiers
54        .unwrap_or_default()
55        .into_iter()
56        .filter_map(|q| match q {
57            "async" => Some(Qualifier::Async),
58            "const" => Some(Qualifier::Const),
59            "unsafe" => Some(Qualifier::Unsafe),
60            _ => None,
61        })
62        .collect::<HashSet<_>>();
63
64    let (i, name) = opt(preceded(multispace1, parse_symbol))(i)?;
65    let (i, mut decl) = opt(preceded(multispace0, parse_function))(i)?;
66
67    decl.as_mut().map(|d| d.qualifiers = qualifiers);
68
69    let query = Query {
70        name,
71        kind: decl.map(QueryKind::FunctionQuery),
72    };
73    Ok((i, query))
74}
75
76fn parse_function<'a, E>(i: &'a str) -> IResult<&'a str, Function, E>
77where
78    E: ParseError<&'a str> + ContextError<&'a str>,
79{
80    let (i, decl) = parse_function_decl(i)?;
81
82    let function = Function {
83        decl,
84        qualifiers: HashSet::new(),
85    };
86    Ok((i, function))
87}
88
89fn parse_function_decl<'a, E>(i: &'a str) -> IResult<&'a str, FnDecl, E>
90where
91    E: ParseError<&'a str> + ContextError<&'a str>,
92{
93    let (i, inputs) = delimited(
94        char('('),
95        alt((
96            value(None, tag("..")),
97            opt(parse_arguments),
98            value(Some(Vec::new()), not(eof)),
99        )),
100        char(')'),
101    )(i)?;
102    let (i, output) = opt(parse_output)(i)?;
103
104    let decl = FnDecl { inputs, output };
105    Ok((i, decl))
106}
107
108fn parse_arguments<'a, E>(i: &'a str) -> IResult<&'a str, Vec<Argument>, E>
109where
110    E: ParseError<&'a str> + ContextError<&'a str>,
111{
112    separated_list0(
113        char(','),
114        preceded(
115            multispace0,
116            alt((
117                parse_argument,
118                value(
119                    Argument {
120                        ty: None,
121                        name: None,
122                    },
123                    char('_'),
124                ),
125                map(parse_type, |ty| Argument {
126                    ty: Some(ty),
127                    name: None,
128                }),
129            )),
130        ),
131    )(i)
132}
133
134fn parse_argument<'a, E>(i: &'a str) -> IResult<&'a str, Argument, E>
135where
136    E: ParseError<&'a str> + ContextError<&'a str>,
137{
138    let (i, name) = alt((value(None, char('_')), opt(parse_symbol)))(i)?;
139    let (i, _) = char(':')(i)?;
140    let (i, _) = multispace0(i)?;
141    let (i, ty) = alt((value(None, char('_')), opt(parse_type)))(i)?;
142
143    let arg = Argument { ty, name };
144    Ok((i, arg))
145}
146
147fn parse_output<'a, E>(i: &'a str) -> IResult<&'a str, FnRetTy, E>
148where
149    E: ParseError<&'a str> + ContextError<&'a str>,
150{
151    preceded(
152        multispace0,
153        alt((
154            value(
155                FnRetTy::DefaultReturn,
156                preceded(preceded(tag("->"), multispace0), tag("()")),
157            ),
158            map(preceded(tag("->"), parse_type), FnRetTy::Return),
159            value(FnRetTy::DefaultReturn, eof),
160        )),
161    )(i)
162}
163
164fn parse_type<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
165where
166    E: ParseError<&'a str> + ContextError<&'a str>,
167{
168    preceded(
169        multispace0,
170        alt((
171            map(parse_primitive_type, Type::Primitive),
172            parse_generic_type,
173            parse_unresolved_path,
174            parse_tuple,
175            parse_slice,
176            value(Type::Never, char('!')),
177            parse_raw_pointer,
178            parse_borrowed_ref,
179        )),
180    )(i)
181}
182
183fn parse_tuple<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
184where
185    E: ParseError<&'a str> + ContextError<&'a str>,
186{
187    map(
188        delimited(
189            char('('),
190            separated_list0(
191                char(','),
192                preceded(
193                    multispace0,
194                    alt((value(None, tag("_")), map(parse_type, Some))),
195                ),
196            ),
197            char(')'),
198        ),
199        Type::Tuple,
200    )(i)
201}
202
203fn parse_slice<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
204where
205    E: ParseError<&'a str> + ContextError<&'a str>,
206{
207    map(
208        delimited(
209            char('['),
210            alt((value(None, tag("_")), map(parse_type, Some))),
211            char(']'),
212        ),
213        |ty| Type::Slice(ty.map(Box::new)),
214    )(i)
215}
216
217fn parse_raw_pointer<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
218where
219    E: ParseError<&'a str> + ContextError<&'a str>,
220{
221    let (i, mutable) = alt((value(true, tag("*mut")), value(false, tag("*const"))))(i)?;
222    let (i, type_) = parse_type(i)?;
223
224    Ok((
225        i,
226        Type::RawPointer {
227            mutable,
228            type_: Box::new(type_),
229        },
230    ))
231}
232
233fn parse_borrowed_ref<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
234where
235    E: ParseError<&'a str> + ContextError<&'a str>,
236{
237    let (i, mutable) = alt((value(true, tag("&mut")), value(false, tag("&"))))(i)?;
238    let (i, type_) = parse_type(i)?;
239
240    Ok((
241        i,
242        Type::BorrowedRef {
243            mutable,
244            type_: Box::new(type_),
245        },
246    ))
247}
248
249fn parse_unresolved_path<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
250where
251    E: ParseError<&'a str> + ContextError<&'a str>,
252{
253    let (i, name) = parse_symbol(i)?;
254    let (i, args) = opt(parse_generic_args)(i)?;
255
256    Ok((
257        i,
258        Type::UnresolvedPath {
259            name,
260            args: args.map(Box::new),
261        },
262    ))
263}
264
265fn parse_generic_args<'a, E>(i: &'a str) -> IResult<&'a str, GenericArgs, E>
266where
267    E: ParseError<&'a str> + ContextError<&'a str>,
268{
269    map(
270        delimited(
271            char('<'),
272            separated_list0(
273                char(','),
274                preceded(
275                    multispace0,
276                    alt((
277                        value(None, tag("_")),
278                        opt(map(parse_type, GenericArg::Type)),
279                    )),
280                ),
281            ),
282            char('>'),
283        ),
284        |args| GenericArgs::AngleBracketed { args },
285    )(i)
286}
287
288fn parse_generic_type<'a, E>(i: &'a str) -> IResult<&'a str, Type, E>
289where
290    E: ParseError<&'a str> + ContextError<&'a str>,
291{
292    let (i, gen) = map(take_while1(|c: char| c.is_ascii_uppercase()), |s: &str| {
293        Type::Generic(s.to_owned())
294    })(i)?;
295
296    if i.chars().next().is_some_and(|c| c.is_ascii_lowercase()) {
297        fail(i)
298    } else {
299        Ok((i, gen))
300    }
301}
302
303fn parse_primitive_type<'a, E>(i: &'a str) -> IResult<&'a str, PrimitiveType, E>
304where
305    E: ParseError<&'a str> + ContextError<&'a str>,
306{
307    use PrimitiveType::*;
308    alt((
309        value(Isize, tag("isize")),
310        value(I8, tag("i8")),
311        value(I16, tag("i16")),
312        value(I32, tag("i32")),
313        value(I64, tag("i64")),
314        value(I128, tag("i128")),
315        value(Usize, tag("usize")),
316        value(U8, tag("u8")),
317        value(U16, tag("u16")),
318        value(U32, tag("u32")),
319        value(U64, tag("u64")),
320        value(U128, tag("u128")),
321        value(F32, tag("f32")),
322        value(F64, tag("f64")),
323        value(Char, tag("char")),
324        value(Bool, tag("bool")),
325        value(Str, tag("str")),
326    ))(i)
327}
328
329#[cfg(test)]
330mod tests {
331    use super::*;
332
333    #[test]
334    fn test_parse_complex_type1() {
335        let input = "&mut [Option<i32>]";
336        let (_, ty) = parse_type::<nom::error::VerboseError<&str>>(input).unwrap();
337        assert_eq!(
338            ty,
339            Type::BorrowedRef {
340                mutable: true,
341                type_: Box::new(Type::Slice(Some(Box::new(Type::UnresolvedPath {
342                    name: "Option".to_string(),
343                    args: Some(Box::new(GenericArgs::AngleBracketed {
344                        args: vec![Some(GenericArg::Type(Type::Primitive(PrimitiveType::I32)))]
345                    }))
346                }))))
347            }
348        );
349    }
350
351    #[test]
352    fn test_parse_complex_type2() {
353        let input = "*const (i32, &str, T)";
354        let (_, ty) = parse_type::<nom::error::VerboseError<&str>>(input).unwrap();
355        assert_eq!(
356            ty,
357            Type::RawPointer {
358                mutable: false,
359                type_: Box::new(Type::Tuple(vec![
360                    Some(Type::Primitive(PrimitiveType::I32)),
361                    Some(Type::BorrowedRef {
362                        mutable: false,
363                        type_: Box::new(Type::Primitive(PrimitiveType::Str)),
364                    }),
365                    Some(Type::Generic("T".to_string())),
366                ]))
367            }
368        );
369    }
370
371    #[test]
372    fn test_parse_complex_type3() {
373        let input = "Result<_, E>";
374        let (_, ty) = parse_type::<nom::error::VerboseError<&str>>(input).unwrap();
375        assert_eq!(
376            ty,
377            Type::UnresolvedPath {
378                name: "Result".to_string(),
379                args: Some(Box::new(GenericArgs::AngleBracketed {
380                    args: vec![None, Some(GenericArg::Type(Type::Generic("E".to_string()))),]
381                }))
382            }
383        );
384    }
385
386    #[test]
387    fn test_parse_function_decl() {
388        let input = "(x: i32, y: &str) -> bool";
389        let (_, decl) = parse_function_decl::<nom::error::VerboseError<&str>>(input).unwrap();
390        assert_eq!(
391            decl,
392            FnDecl {
393                inputs: Some(vec![
394                    Argument {
395                        name: Some("x".to_string()),
396                        ty: Some(Type::Primitive(PrimitiveType::I32)),
397                    },
398                    Argument {
399                        name: Some("y".to_string()),
400                        ty: Some(Type::BorrowedRef {
401                            mutable: false,
402                            type_: Box::new(Type::Primitive(PrimitiveType::Str)),
403                        }),
404                    },
405                ]),
406                output: Some(FnRetTy::Return(Type::Primitive(PrimitiveType::Bool))),
407            }
408        );
409    }
410
411    #[test]
412    fn test_parse_function_decl_with_underscore() {
413        let input = "(_, y: &str) -> ()";
414        let (_, decl) = parse_function_decl::<nom::error::VerboseError<&str>>(input).unwrap();
415        assert_eq!(
416            decl,
417            FnDecl {
418                inputs: Some(vec![
419                    Argument {
420                        name: None,
421                        ty: None,
422                    },
423                    Argument {
424                        name: Some("y".to_string()),
425                        ty: Some(Type::BorrowedRef {
426                            mutable: false,
427                            type_: Box::new(Type::Primitive(PrimitiveType::Str)),
428                        }),
429                    },
430                ]),
431                output: Some(FnRetTy::DefaultReturn),
432            }
433        );
434    }
435
436    #[test]
437    fn test_parse_complex_output_type() {
438        let input = "(x: i32) -> (i32, &str, T)";
439        let (_, decl) = parse_function_decl::<nom::error::VerboseError<&str>>(input).unwrap();
440        assert_eq!(
441            decl,
442            FnDecl {
443                inputs: Some(vec![Argument {
444                    name: Some("x".to_string()),
445                    ty: Some(Type::Primitive(PrimitiveType::I32)),
446                },]),
447                output: Some(FnRetTy::Return(Type::Tuple(vec![
448                    Some(Type::Primitive(PrimitiveType::I32)),
449                    Some(Type::BorrowedRef {
450                        mutable: false,
451                        type_: Box::new(Type::Primitive(PrimitiveType::Str)),
452                    }),
453                    Some(Type::Generic("T".to_string())),
454                ]))),
455            }
456        );
457    }
458
459    #[test]
460    fn test_parse_complex_output_type2() {
461        let input = "fn abc() -> Result<Vec<i32>>";
462        let (_, decl) = parse_query(input).unwrap();
463        assert_eq!(
464            decl,
465            Query {
466                name: Some("abc".to_string()),
467                kind: Some(QueryKind::FunctionQuery(Function {
468                    decl: FnDecl {
469                        inputs: Some(vec![]),
470                        output: Some(FnRetTy::Return(Type::UnresolvedPath {
471                            name: "Result".to_string(),
472                            args: Some(Box::new(GenericArgs::AngleBracketed {
473                                args: vec![Some(GenericArg::Type(Type::UnresolvedPath {
474                                    name: "Vec".to_string(),
475                                    args: Some(Box::new(GenericArgs::AngleBracketed {
476                                        args: vec![Some(GenericArg::Type(Type::Primitive(
477                                            PrimitiveType::I32
478                                        )))]
479                                    }))
480                                }))]
481                            }))
482                        })),
483                    },
484                    qualifiers: HashSet::new(),
485                })),
486            }
487        );
488    }
489
490    #[test]
491    fn test_parse_qualified_function() {
492        let input = "pub async fn foo(bar: i32, _: &str) -> bool";
493        let (_, query) = parse_query(input).unwrap();
494        assert_eq!(
495            query,
496            Query {
497                name: Some("foo".to_string()),
498                kind: Some(QueryKind::FunctionQuery(Function {
499                    decl: FnDecl {
500                        inputs: Some(vec![
501                            Argument {
502                                name: Some("bar".to_string()),
503                                ty: Some(Type::Primitive(PrimitiveType::I32)),
504                            },
505                            Argument {
506                                name: None,
507                                ty: Some(Type::BorrowedRef {
508                                    mutable: false,
509                                    type_: Box::new(Type::Primitive(PrimitiveType::Str)),
510                                }),
511                            },
512                        ]),
513                        output: Some(FnRetTy::Return(Type::Primitive(PrimitiveType::Bool))),
514                    },
515                    qualifiers: HashSet::from_iter(vec![Qualifier::Async]),
516                })),
517            }
518        );
519    }
520}