Skip to main content

nickel_lang_parser/ast/
typ.rs

1//! Representation of Nickel types in the AST.
2use super::{Ast, AstAlloc, TermPos};
3pub use crate::typ::{EnumRowF, EnumRowsF, RecordRowF, RecordRowsF, TypeF};
4use crate::{identifier::Ident, impl_display_from_bytecode_pretty, traverse::*};
5use iter::*;
6
7/// The recursive unrolling of a type, that is when we "peel off" the top-level layer to find the actual
8/// structure represented by an instantiation of `TypeF`.
9pub type TypeUnr<'ast> = TypeF<&'ast Type<'ast>, RecordRows<'ast>, EnumRows<'ast>, &'ast Ast<'ast>>;
10
11/// The recursive unrolling of a enum rows.
12pub type EnumRowsUnr<'ast> = EnumRowsF<&'ast Type<'ast>, &'ast EnumRows<'ast>>;
13
14/// The recursive unrolling of record rows.
15pub type RecordRowsUnr<'ast> = RecordRowsF<&'ast Type<'ast>, &'ast RecordRows<'ast>>;
16
17/// Concrete, recursive definition for an enum row.
18pub type EnumRow<'ast> = EnumRowF<&'ast Type<'ast>>;
19/// Concrete, recursive definition for enum rows.
20#[derive(Clone, PartialEq, Eq, Debug)]
21pub struct EnumRows<'ast>(pub EnumRowsUnr<'ast>);
22/// Concrete, recursive definition for a record row.
23pub type RecordRow<'ast> = RecordRowF<&'ast Type<'ast>>;
24#[derive(Clone, PartialEq, Eq, Debug)]
25/// Concrete, recursive definition for record rows.
26pub struct RecordRows<'ast>(pub RecordRowsUnr<'ast>);
27
28/// Concrete, recursive type for a Nickel type.
29#[derive(Clone, PartialEq, Eq, Debug)]
30pub struct Type<'ast> {
31    pub typ: TypeUnr<'ast>,
32    pub pos: TermPos,
33}
34
35impl<'ast> From<TypeUnr<'ast>> for Type<'ast> {
36    fn from(typ: TypeUnr<'ast>) -> Self {
37        Type {
38            typ,
39            pos: TermPos::None,
40        }
41    }
42}
43
44impl<'ast> Type<'ast> {
45    /// Sets a new position for this type.
46    pub fn with_pos(self, pos: TermPos) -> Type<'ast> {
47        Type { pos, ..self }
48    }
49
50    /// Searches for a [crate::typ::TypeF]. If one is found, returns the term it contains.
51    pub fn find_contract(&'ast self) -> Option<&'ast Ast<'ast>> {
52        self.find_map(|ty: &'ast Type| match &ty.typ {
53            TypeF::Contract(f) => Some(*f),
54            _ => None,
55        })
56    }
57}
58
59impl<'ast> TypeUnr<'ast> {
60    pub fn spanned(self, pos: TermPos) -> Type<'ast> {
61        Type { typ: self, pos }
62    }
63}
64
65impl<'ast> TraverseAlloc<'ast, Type<'ast>> for Type<'ast> {
66    fn traverse<F, E>(
67        self,
68        alloc: &'ast AstAlloc,
69        f: &mut F,
70        order: TraverseOrder,
71    ) -> Result<Self, E>
72    where
73        F: FnMut(Type<'ast>) -> Result<Type<'ast>, E>,
74    {
75        let pre_map = match order {
76            TraverseOrder::TopDown => f(self)?,
77            TraverseOrder::BottomUp => self,
78        };
79
80        // traverse keeps track of state in the FnMut function. try_map_state
81        // keeps track of it in a separate state variable. we can pass the
82        // former into the latter by treating the function itself as the state
83        let typ = pre_map.typ.try_map_state(
84            |ty, f| Ok(alloc.alloc(ty.clone().traverse(alloc, f, order)?)),
85            |rrows, f| rrows.traverse(alloc, f, order),
86            |erows, _| Ok(erows),
87            |ctr, _| Ok(ctr),
88            f,
89        )?;
90
91        let post_map = Type { typ, ..pre_map };
92
93        match order {
94            TraverseOrder::TopDown => Ok(post_map),
95            TraverseOrder::BottomUp => f(post_map),
96        }
97    }
98
99    fn traverse_ref<S, U>(
100        &'ast self,
101        f: &mut dyn FnMut(&'ast Type<'ast>, &S) -> TraverseControl<S, U>,
102        state: &S,
103    ) -> Option<U> {
104        let child_state = match f(self, state) {
105            TraverseControl::Continue => None,
106            TraverseControl::ContinueWithScope(s) => Some(s),
107            TraverseControl::SkipBranch => {
108                return None;
109            }
110            TraverseControl::Return(ret) => {
111                return Some(ret);
112            }
113        };
114        let state = child_state.as_ref().unwrap_or(state);
115
116        match &self.typ {
117            TypeF::Dyn
118            | TypeF::Number
119            | TypeF::Bool
120            | TypeF::String
121            | TypeF::ForeignId
122            | TypeF::Symbol
123            | TypeF::Var(_)
124            | TypeF::Enum(_)
125            | TypeF::Wildcard(_) => None,
126            TypeF::Contract(ast) => ast.traverse_ref(f, state),
127            TypeF::Arrow(t1, t2) => t1
128                .traverse_ref(f, state)
129                .or_else(|| t2.traverse_ref(f, state)),
130            TypeF::Forall { body: t, .. }
131            | TypeF::Dict { type_fields: t, .. }
132            | TypeF::Array(t) => t.traverse_ref(f, state),
133            TypeF::Record(rrows) => rrows.traverse_ref(f, state),
134        }
135    }
136}
137
138impl<'ast> TraverseAlloc<'ast, Ast<'ast>> for Type<'ast> {
139    fn traverse<F, E>(
140        self,
141        alloc: &'ast AstAlloc,
142        f: &mut F,
143        order: TraverseOrder,
144    ) -> Result<Self, E>
145    where
146        F: FnMut(Ast<'ast>) -> Result<Ast<'ast>, E>,
147    {
148        self.traverse(
149            alloc,
150            &mut |ty: Type| match ty.typ {
151                TypeF::Contract(t) => t
152                    .clone()
153                    .traverse(alloc, f, order)
154                    .map(|t| Type::from(TypeF::Contract(alloc.alloc(t))).with_pos(ty.pos)),
155                _ => Ok(ty),
156            },
157            order,
158        )
159    }
160
161    fn traverse_ref<S, U>(
162        &'ast self,
163        f: &mut dyn FnMut(&'ast Ast<'ast>, &S) -> TraverseControl<S, U>,
164        state: &S,
165    ) -> Option<U> {
166        self.traverse_ref(
167            &mut |ty: &'ast Type, s: &S| match &ty.typ {
168                TypeF::Contract(t) => {
169                    if let Some(ret) = t.traverse_ref(f, s) {
170                        TraverseControl::Return(ret)
171                    } else {
172                        TraverseControl::SkipBranch
173                    }
174                }
175                _ => TraverseControl::Continue,
176            },
177            state,
178        )
179    }
180}
181
182impl<'ast> TraverseAlloc<'ast, Type<'ast>> for RecordRows<'ast> {
183    fn traverse<F, E>(
184        self,
185        alloc: &'ast AstAlloc,
186        f: &mut F,
187        order: TraverseOrder,
188    ) -> Result<RecordRows<'ast>, E>
189    where
190        F: FnMut(Type<'ast>) -> Result<Type<'ast>, E>,
191    {
192        // traverse keeps track of state in the FnMut function. try_map_state
193        // keeps track of it in a separate state variable. we can pass the
194        // former into the latter by treating the function itself as the state
195        let rows = self.0.try_map_state(
196            |ty, f| Ok(alloc.alloc(ty.clone().traverse(alloc, f, order)?)),
197            |rrows, f| Ok(alloc.alloc(rrows.clone().traverse(alloc, f, order)?)),
198            f,
199        )?;
200
201        Ok(RecordRows(rows))
202    }
203
204    fn traverse_ref<S, U>(
205        &'ast self,
206        f: &mut dyn FnMut(&'ast Type<'ast>, &S) -> TraverseControl<S, U>,
207        state: &S,
208    ) -> Option<U> {
209        match &self.0 {
210            RecordRowsF::Extend { row, tail } => row
211                .typ
212                .traverse_ref(f, state)
213                .or_else(|| tail.traverse_ref(f, state)),
214            _ => None,
215        }
216    }
217}
218
219impl<'ast> RecordRows<'ast> {
220    /// Find a nested binding in a record row type. The nested field is given as a list of
221    /// successive fields, that is, as a path. Return `None` if there is no such binding.
222    ///
223    /// # Example
224    ///
225    /// - self: `{a : {b : Number }}`
226    /// - path: `["a", "b"]`
227    /// - result: `Some(Number)`
228    pub fn find_path<'a>(&'a self, path: &[Ident]) -> Option<&'a RecordRow<'ast>> {
229        let mut curr_rrows = self;
230
231        for (idx, id) in path.iter().enumerate() {
232            let next_rrows = curr_rrows.iter().find_map(|item| match item {
233                RecordRowsItem::Row(row) if row.id.ident() == *id => Some(row),
234                _ => None,
235            });
236
237            if idx == path.len() - 1 {
238                return next_rrows;
239            }
240
241            match next_rrows.map(|row| &row.typ.typ) {
242                Some(TypeF::Record(rrows)) => curr_rrows = rrows,
243                _ => return None,
244            }
245        }
246
247        None
248    }
249
250    /// Find the row with the given identifier in the record type. Return `None` if there is no such
251    /// row.
252    ///
253    /// Equivalent to `find_path(&[id])`.
254    pub fn find_row(&self, id: Ident) -> Option<&RecordRow<'ast>> {
255        self.find_path(&[id])
256    }
257
258    pub fn iter(&self) -> RecordRowsIter<'_, Type<'ast>, RecordRows<'ast>> {
259        RecordRowsIter {
260            rrows: Some(self),
261            ty: std::marker::PhantomData,
262        }
263    }
264}
265
266impl<'ast> EnumRows<'ast> {
267    /// Find the row with the given identifier in the enum type. Return `None` if there is no such
268    /// row.
269    pub fn find_row<'a>(&'a self, id: Ident) -> Option<&'a EnumRow<'ast>> {
270        self.iter().find_map(|row_item| match row_item {
271            EnumRowsItem::Row(row) if row.id.ident() == id => Some(row),
272            _ => None,
273        })
274    }
275
276    pub fn iter(&self) -> EnumRowsIter<'_, Type<'ast>, EnumRows<'ast>> {
277        EnumRowsIter {
278            erows: Some(self),
279            ty: std::marker::PhantomData,
280        }
281    }
282}
283
284impl_display_from_bytecode_pretty!(Type<'_>);
285impl_display_from_bytecode_pretty!(EnumRow<'_>);
286impl_display_from_bytecode_pretty!(EnumRows<'_>);
287impl_display_from_bytecode_pretty!(RecordRow<'_>);
288impl_display_from_bytecode_pretty!(RecordRows<'_>);
289
290pub mod iter {
291    use super::*;
292    use crate::identifier::LocIdent;
293
294    /// An iterator over the rows of a record type.
295    pub struct RecordRowsIter<'a, Ty, RRows> {
296        pub(crate) rrows: Option<&'a RRows>,
297        pub(crate) ty: std::marker::PhantomData<Ty>,
298    }
299
300    /// The item produced by an iterator over record rows.
301    pub enum RecordRowsItem<'a, Ty> {
302        TailDyn,
303        TailVar(&'a LocIdent),
304        Row(&'a RecordRowF<Ty>),
305    }
306
307    impl<'a, 'ast> Iterator for RecordRowsIter<'a, Type<'ast>, RecordRows<'ast>> {
308        type Item = RecordRowsItem<'a, &'ast Type<'ast>>;
309
310        fn next(&mut self) -> Option<Self::Item> {
311            self.rrows.and_then(|next| match next.0 {
312                RecordRowsF::Empty => {
313                    self.rrows = None;
314                    None
315                }
316                RecordRowsF::TailDyn => {
317                    self.rrows = None;
318                    Some(RecordRowsItem::TailDyn)
319                }
320                RecordRowsF::TailVar(ref id) => {
321                    self.rrows = None;
322                    Some(RecordRowsItem::TailVar(id))
323                }
324                RecordRowsF::Extend { ref row, tail } => {
325                    self.rrows = Some(tail);
326                    Some(RecordRowsItem::Row(row))
327                }
328            })
329        }
330    }
331
332    pub struct EnumRowsIter<'a, Ty, ERows> {
333        pub(crate) erows: Option<&'a ERows>,
334        pub(crate) ty: std::marker::PhantomData<Ty>,
335    }
336
337    pub enum EnumRowsItem<'a, Ty> {
338        TailVar(&'a LocIdent),
339        Row(&'a EnumRowF<Ty>),
340    }
341
342    impl<'a, 'ast> Iterator for EnumRowsIter<'a, Type<'ast>, EnumRows<'ast>> {
343        type Item = EnumRowsItem<'a, &'ast Type<'ast>>;
344
345        fn next(&mut self) -> Option<Self::Item> {
346            self.erows.and_then(|next| match next.0 {
347                EnumRowsF::Empty => {
348                    self.erows = None;
349                    None
350                }
351                EnumRowsF::TailVar(ref id) => {
352                    self.erows = None;
353                    Some(EnumRowsItem::TailVar(id))
354                }
355                EnumRowsF::Extend { ref row, tail } => {
356                    self.erows = Some(tail);
357                    Some(EnumRowsItem::Row(row))
358                }
359            })
360        }
361    }
362}