Skip to main content

dada_parser/
types.rs

1use salsa::Update;
2
3use dada_ir_ast::{
4    ast::{
5        AstGenericDecl, AstGenericKind, AstGenericTerm, AstPath, AstPerm, AstPermKind, AstTy,
6        AstTyKind, SpanVec,
7    },
8    span::{Span, Spanned},
9};
10
11use super::{
12    Expected, Parse, ParseFail, Parser,
13    tokenizer::{Delimiter, Keyword},
14};
15
16// Parsing types and permissions is annoying.
17// Declare a cover grammar first.
18#[derive(Debug, Update)]
19enum TyOrPerm<'db> {
20    /// could be anything from `a` to `a.b` to `a[x]` to `a.b[x]`
21    Path(AstPath<'db>, Option<SpanVec<'db, AstGenericTerm<'db>>>),
22
23    /// `type T` or `perm P`
24    Generic(AstGenericDecl<'db>),
25
26    /// Perm that starts with a keyword, like `my`
27    PermKeyword(AstPerm<'db>),
28
29    /// P1 P2
30    Apply(AstPerm<'db>, AstTy<'db>),
31}
32
33impl<'db> Parse<'db> for TyOrPerm<'db> {
34    type Output = Self;
35
36    fn opt_parse(
37        db: &'db dyn crate::Db,
38        parser: &mut Parser<'_, 'db>,
39    ) -> Result<Option<Self::Output>, ParseFail<'db>> {
40        if let Some(path) = AstPath::opt_parse(db, parser)? {
41            let generic_args = AstGenericTerm::opt_parse_delimited(
42                db,
43                parser,
44                Delimiter::SquareBrackets,
45                AstGenericTerm::eat_comma,
46            )?;
47
48            return TyOrPerm::Path(path, generic_args).maybe_apply(db, parser);
49        }
50
51        if let Some(generic_decl) = AstGenericDecl::opt_parse(db, parser)? {
52            return TyOrPerm::Generic(generic_decl).maybe_apply(db, parser);
53        };
54
55        if let Some(p) = KeywordPerm::opt_parse(db, parser)? {
56            return TyOrPerm::PermKeyword(p).maybe_apply(db, parser);
57        }
58
59        Ok(None)
60    }
61
62    fn expected() -> Expected {
63        Expected::Nonterminal("type or permission")
64    }
65}
66
67impl<'db> Spanned<'db> for TyOrPerm<'db> {
68    fn span(&self, db: &'db dyn crate::Db) -> Span<'db> {
69        match self {
70            TyOrPerm::Path(path, args) => {
71                let args_span = args.as_ref().map(|a| a.span);
72                path.span(db).to(db, args_span)
73            }
74            TyOrPerm::Generic(decl) => decl.span(db),
75            TyOrPerm::PermKeyword(p) => p.span(db),
76            TyOrPerm::Apply(p, ty) => p.span(db).to(db, ty.span(db)),
77        }
78    }
79}
80
81impl<'db> TyOrPerm<'db> {
82    /// If this could be a permission and it is followed by a type, parse it as an application.
83    fn maybe_apply(
84        self,
85        db: &'db dyn crate::Db,
86        parser: &mut Parser<'_, 'db>,
87    ) -> Result<Option<Self>, ParseFail<'db>> {
88        if self.can_be_perm(db)
89            && parser.next_token_on_same_line()
90            && let Some(ty) = AstTy::opt_parse(db, parser)?
91        {
92            let perm = self.into_perm(db).unwrap();
93            return Ok(Some(TyOrPerm::Apply(perm, ty)));
94        }
95
96        Ok(Some(self))
97    }
98
99    /// True if this could syntactically be a permission.
100    fn can_be_perm(&self, db: &'db dyn crate::Db) -> bool {
101        match self {
102            TyOrPerm::Path(path, None) => path.len(db) == 1,
103            TyOrPerm::Path(_path, Some(_)) => false,
104            TyOrPerm::Generic(decl) => matches!(decl.kind(db), AstGenericKind::Perm(_)),
105            TyOrPerm::PermKeyword(_) => true,
106            TyOrPerm::Apply(_, _) => false,
107        }
108    }
109
110    fn into_perm(self, db: &'db dyn crate::Db) -> Option<AstPerm<'db>> {
111        match self {
112            TyOrPerm::Path(path, None) if path.len(db) == 1 => {
113                let id = path.first_id(db);
114                Some(AstPerm::new(db, id.span, AstPermKind::Variable(id)))
115            }
116            TyOrPerm::Path(..) => None,
117            TyOrPerm::Generic(decl) => match decl.kind(db) {
118                AstGenericKind::Perm(_) => Some(AstPerm::new(
119                    db,
120                    decl.span(db),
121                    AstPermKind::GenericDecl(decl),
122                )),
123                _ => None,
124            },
125            TyOrPerm::PermKeyword(p) => Some(p),
126            TyOrPerm::Apply(_, _) => None,
127        }
128    }
129
130    /// True if this could syntactically be a permission.
131    fn can_be_ty(&self, db: &'db dyn crate::Db) -> bool {
132        match self {
133            TyOrPerm::Path(..) => true,
134            TyOrPerm::Generic(decl) => matches!(decl.kind(db), AstGenericKind::Type(_)),
135            TyOrPerm::PermKeyword(_) => false,
136            TyOrPerm::Apply(_, _) => true,
137        }
138    }
139
140    fn into_ty(self, db: &'db dyn crate::Db) -> Option<AstTy<'db>> {
141        let span = self.span(db);
142        match self {
143            TyOrPerm::Path(path, args) => Some(AstTy::new(db, span, AstTyKind::Named(path, args))),
144            TyOrPerm::Generic(decl) => match decl.kind(db) {
145                AstGenericKind::Type(_) => Some(AstTy::new(db, span, AstTyKind::GenericDecl(decl))),
146                _ => None,
147            },
148            TyOrPerm::PermKeyword(_) => None,
149            TyOrPerm::Apply(p, t) => Some(AstTy::new(db, span, AstTyKind::Perm(p, t))),
150        }
151    }
152}
153
154impl<'db> Parse<'db> for AstTy<'db> {
155    type Output = Self;
156
157    fn opt_parse(
158        db: &'db dyn crate::Db,
159        parser: &mut Parser<'_, 'db>,
160    ) -> Result<Option<Self>, ParseFail<'db>> {
161        let Some(ty_or_perm) = TyOrPerm::opt_parse(db, parser)? else {
162            return Ok(None);
163        };
164
165        let span = ty_or_perm.span(db);
166
167        if let Some(ty) = ty_or_perm.into_ty(db) {
168            return Ok(Some(ty));
169        }
170
171        Err(ParseFail::Expected(span, Self::expected()))
172    }
173
174    fn expected() -> Expected {
175        Expected::Nonterminal("type")
176    }
177}
178
179impl<'db> Parse<'db> for AstPerm<'db> {
180    type Output = Self;
181
182    fn opt_parse(
183        db: &'db dyn crate::Db,
184        parser: &mut Parser<'_, 'db>,
185    ) -> Result<Option<Self>, ParseFail<'db>> {
186        let Some(ty_or_perm) = TyOrPerm::opt_parse(db, parser)? else {
187            return Ok(None);
188        };
189
190        let span = ty_or_perm.span(db);
191
192        if let Some(perm) = ty_or_perm.into_perm(db) {
193            return Ok(Some(perm));
194        }
195
196        Err(ParseFail::Expected(span, Self::expected()))
197    }
198
199    fn expected() -> Expected {
200        Expected::Nonterminal("permission")
201    }
202}
203
204struct KeywordPerm;
205
206impl<'db> Parse<'db> for KeywordPerm {
207    type Output = AstPerm<'db>;
208
209    fn opt_parse(
210        db: &'db dyn crate::Db,
211        tokens: &mut Parser<'_, 'db>,
212    ) -> Result<Option<AstPerm<'db>>, ParseFail<'db>> {
213        if let Ok(span) = tokens.eat_keyword(Keyword::Ref) {
214            Ok(Some(parse_path_perm(
215                db,
216                span,
217                tokens,
218                AstPermKind::Referenced,
219            )?))
220        } else if let Ok(span) = tokens.eat_keyword(Keyword::Mut) {
221            Ok(Some(parse_path_perm(
222                db,
223                span,
224                tokens,
225                AstPermKind::Mutable,
226            )?))
227        } else if let Ok(span) = tokens.eat_keyword(Keyword::Given) {
228            Ok(Some(parse_path_perm(db, span, tokens, AstPermKind::Given)?))
229        } else if let Ok(span) = tokens.eat_keyword(Keyword::My) {
230            Ok(Some(AstPerm::new(db, span, AstPermKind::My)))
231        } else if let Ok(span) = tokens.eat_keyword(Keyword::Our) {
232            Ok(Some(AstPerm::new(db, span, AstPermKind::Our)))
233        } else {
234            Ok(None)
235        }
236    }
237
238    fn expected() -> Expected {
239        AstPerm::expected()
240    }
241}
242
243fn parse_path_perm<'db>(
244    db: &'db dyn crate::Db,
245    span: Span<'db>,
246    parser: &mut Parser<'_, 'db>,
247    op: impl Fn(Option<SpanVec<'db, AstPath<'db>>>) -> AstPermKind<'db>,
248) -> Result<AstPerm<'db>, ParseFail<'db>> {
249    let paths =
250        AstPath::opt_parse_delimited(db, parser, Delimiter::SquareBrackets, AstPath::eat_comma)?;
251    let kind = op(paths);
252    Ok(AstPerm::new(db, span.to(db, parser.last_span()), kind))
253}
254
255impl<'db> Parse<'db> for AstGenericTerm<'db> {
256    type Output = AstGenericTerm<'db>;
257
258    fn opt_parse(
259        db: &'db dyn crate::Db,
260        parser: &mut Parser<'_, 'db>,
261    ) -> Result<Option<Self::Output>, ParseFail<'db>> {
262        let Some(ty_or_perm) = TyOrPerm::opt_parse(db, parser)? else {
263            return Ok(None);
264        };
265
266        match ty_or_perm {
267            // There is one case that could be either a type or a permission.
268            TyOrPerm::Path(path, None) if path.len(db) == 1 => {
269                Ok(Some(AstGenericTerm::Id(path.first_id(db))))
270            }
271
272            // For the rest, we can be guided by the syntax.
273            TyOrPerm::Generic(_)
274            | TyOrPerm::PermKeyword(_)
275            | TyOrPerm::Path(..)
276            | TyOrPerm::Apply(_, _) => {
277                let can_be_perm = ty_or_perm.can_be_perm(db);
278                let can_be_ty = ty_or_perm.can_be_ty(db);
279
280                if can_be_perm {
281                    assert!(!can_be_ty);
282                    Ok(Some(ty_or_perm.into_perm(db).unwrap().into()))
283                } else {
284                    assert!(can_be_ty);
285                    Ok(Some(ty_or_perm.into_ty(db).unwrap().into()))
286                }
287            }
288        }
289    }
290
291    fn expected() -> Expected {
292        Expected::Nonterminal("type or permission")
293    }
294}