1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
use super::statement::compound::{self, Compound};
use crate::utility::{
    data_type::{self, Type},
    parsing,
};
use nom::{
    bytes::complete::tag,
    character::complete::{multispace0, space0},
    combinator::map,
    multi::separated_list0,
    sequence::{delimited, pair, tuple},
    IResult,
};

/// [`Parameter`] represents a function's parameter.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct Parameter {
    pub name: String,
    pub data_type: Type,
}

/// Parse source code to get a [`Parameter`].
fn parse_parameter(code: &str) -> IResult<&str, Parameter> {
    map(
        tuple((parsing::ident, space0, tag(":"), space0, data_type::parse)),
        |(name, _, _, _, data_type)| Parameter { name, data_type },
    )(code)
}

/// [`FunctionDefinition`] represents a function definition.
#[derive(Debug, Eq, PartialEq, Clone, Hash)]
pub struct FunctionDefinition {
    pub name: String,
    pub parameters: Vec<Parameter>,
    pub return_type: Type,
    pub content: Compound,
}

/// Parse source code to get a [`FunctionDefinition`].
pub fn parse(code: &str) -> IResult<&str, FunctionDefinition> {
    map(
        tuple((
            tag("fn"),
            space0,
            parsing::ident,
            delimited(
                pair(tag("("), space0),
                separated_list0(parsing::in_multispace(tag(",")), parse_parameter),
                pair(multispace0, tag(")")),
            ),
            parsing::in_multispace(tag("->")),
            data_type::parse,
            parsing::in_multispace(compound::parse),
        )),
        |(_, _, name, parameters, _, return_type, content)| FunctionDefinition {
            name,
            parameters,
            return_type,
            content,
        },
    )(code)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn can_parse() {
        let function_definition = parse(
            "fn add(a: i32, b: i32) -> i32 {
    return a + b;
}",
        )
        .unwrap()
        .1;
        assert_eq!(function_definition.name, "add");
        assert_eq!(function_definition.parameters.len(), 2);
    }
}