Skip to main content

dada_parser/
module_body.rs

1use dada_ir_ast::{
2    ast::{
3        AstAggregate, AstFunction, AstItem, AstMainFunction, AstModule, AstPath, AstStatement,
4        AstUse, SpanVec,
5    },
6    diagnostic::Diagnostic,
7    span::Spanned,
8};
9
10use crate::tokenizer::operator;
11
12use super::{Expected, Parse, ParseFail, Parser, miscellaneous::OrOptParse, tokenizer::Keyword};
13
14impl<'db> Parse<'db> for AstModule<'db> {
15    type Output = Self;
16
17    fn opt_parse(
18        db: &'db dyn crate::Db,
19        parser: &mut Parser<'_, 'db>,
20    ) -> Result<Option<Self>, ParseFail<'db>> {
21        // Derive the name of the module from the source file in the span.
22        let name = parser.last_span().source_file(db).module_name(db);
23
24        // Parse (item* statement*), skipping unrecognized tokens.
25        let mut items: Vec<AstItem<'db>> = vec![];
26        let mut statements = vec![];
27        let start_span = parser.peek_span();
28        while parser.peek().is_some() {
29            if statements.is_empty() {
30                match AstItem::opt_parse(db, parser) {
31                    Ok(Some(v)) => {
32                        items.push(v);
33                        continue;
34                    }
35
36                    Err(e) => {
37                        parser.push_diagnostic(e.into_diagnostic(db));
38                        continue;
39                    }
40
41                    Ok(None) => {}
42                }
43            }
44
45            match AstStatement::opt_parse(db, parser) {
46                Ok(Some(s)) => {
47                    statements.push(s);
48                    continue;
49                }
50
51                Err(e) => {
52                    parser.push_diagnostic(e.into_diagnostic(db));
53                    continue;
54                }
55
56                Ok(None) => {}
57            }
58
59            parser.eat_next_token().unwrap();
60            parser.push_diagnostic(Diagnostic::error(
61                db,
62                parser.last_span(),
63                "expected a statement or a module-level item",
64            ));
65        }
66
67        // If we have statements on their own, wrap them in a `main` function
68        if let Some(first) = statements.first()
69            && let Some(last) = statements.last()
70        {
71            let span = first.span(db).to(db, last.span(db));
72            let main_fn = AstMainFunction::new(
73                db,
74                SpanVec {
75                    span,
76                    values: statements,
77                },
78            );
79            items.push(main_fn.into());
80        }
81
82        Ok(Some(AstModule::new(
83            db,
84            name,
85            SpanVec {
86                span: start_span.to(db, parser.last_span()),
87                values: items,
88            },
89        )))
90    }
91
92    fn expected() -> Expected {
93        panic!("infallible")
94    }
95}
96
97impl<'db> Parse<'db> for AstItem<'db> {
98    type Output = Self;
99
100    fn opt_parse(
101        db: &'db dyn crate::Db,
102        parser: &mut Parser<'_, 'db>,
103    ) -> Result<Option<Self>, ParseFail<'db>> {
104        AstAggregate::opt_parse(db, parser)
105            .or_opt_parse::<Self, AstUse<'db>>(db, parser)
106            .or_opt_parse::<Self, AstFunction<'db>>(db, parser)
107    }
108
109    fn expected() -> Expected {
110        panic!("module-level item (class, function, use)")
111    }
112}
113
114/// use path [as name];
115impl<'db> Parse<'db> for AstUse<'db> {
116    type Output = Self;
117
118    fn opt_parse(
119        db: &'db dyn crate::Db,
120        parser: &mut Parser<'_, 'db>,
121    ) -> Result<Option<Self>, ParseFail<'db>> {
122        let Ok(start) = parser.eat_keyword(Keyword::Use) else {
123            return Ok(None);
124        };
125
126        let crate_name = parser.eat_id()?;
127        let _dot = parser.eat_op(operator::DOT)?;
128        let path = AstPath::eat(db, parser)?;
129
130        let as_id = if parser.eat_keyword(Keyword::As).is_ok() {
131            Some(parser.eat_id()?)
132        } else {
133            None
134        };
135
136        Ok(Some(AstUse::new(
137            db,
138            start.to(db, parser.last_span()),
139            crate_name,
140            path,
141            as_id,
142        )))
143    }
144
145    fn expected() -> Expected {
146        Expected::Keyword(Keyword::Use)
147    }
148}