deskc_hirgen/
lib.rs

1mod error;
2mod extract_includes;
3mod gen_effect_expr;
4pub use extract_includes::extract_includes;
5
6use std::{
7    cell::RefCell,
8    collections::{HashMap, HashSet},
9};
10
11use ast::span::{Span, Spanned};
12use error::HirGenError;
13use file::{FileId, InFile};
14use hir::{
15    expr::{Expr, Handler, Literal, MatchCase},
16    meta::{Id, Meta, WithMeta},
17    ty::Type,
18};
19
20pub fn gen_hir(
21    file_id: FileId,
22    src: &Spanned<ast::expr::Expr>,
23    included: HashMap<String, InFile<Spanned<ast::expr::Expr>>>,
24) -> Result<(HirGen, WithMeta<Expr>), HirGenError> {
25    let hir = HirGen {
26        file_stack: RefCell::new(vec![file_id]),
27        included,
28        ..Default::default()
29    };
30    hir.gen(src).map(|expr| (hir, expr))
31}
32
33#[derive(Default, Debug)]
34pub struct HirGen {
35    next_id: RefCell<Id>,
36    next_span: RefCell<Vec<Span>>,
37    pub variables: RefCell<HashMap<String, Id>>,
38    pub attrs: RefCell<HashMap<Id, Vec<Expr>>>,
39    pub included: HashMap<String, InFile<Spanned<ast::expr::Expr>>>,
40    pub type_aliases: RefCell<HashMap<String, Type>>,
41    brands: RefCell<HashSet<String>>,
42    // current file id is the last item.
43    file_stack: RefCell<Vec<FileId>>,
44}
45
46impl HirGen {
47    pub fn push_file_id(&self, file_id: FileId) {
48        self.file_stack.borrow_mut().push(file_id);
49    }
50    pub fn pop_file_id(&self) -> FileId {
51        self.file_stack.borrow_mut().pop().unwrap()
52    }
53    pub fn gen_type(&self, ty: &Spanned<ast::ty::Type>) -> Result<WithMeta<Type>, HirGenError> {
54        let (ty, span) = ty;
55        self.push_span(span.clone());
56
57        let with_meta = match ty {
58            ast::ty::Type::Number => self.with_meta(Type::Number),
59            ast::ty::Type::String => self.with_meta(Type::String),
60            ast::ty::Type::Trait(types) => self.with_meta(Type::Trait(
61                types
62                    .iter()
63                    .map(|ty| self.gen_type(ty))
64                    .collect::<Result<_, _>>()?,
65            )),
66            ast::ty::Type::Effectful { ty, effects } => self.with_meta(Type::Effectful {
67                ty: Box::new(self.gen_type(ty)?),
68                effects: self.gen_effect_expr(effects)?,
69            }),
70            ast::ty::Type::Infer => self.with_meta(Type::Infer),
71            ast::ty::Type::This => self.with_meta(Type::This),
72            ast::ty::Type::Alias(ident) | ast::ty::Type::Variable(ident) => self.with_meta(
73                self.type_aliases
74                    .borrow()
75                    .get(ident)
76                    .cloned()
77                    .unwrap_or_else(|| Type::Variable(self.get_id_of(ident.clone()))),
78            ),
79            ast::ty::Type::Product(types) => self.with_meta(Type::Product(
80                types
81                    .iter()
82                    .map(|ty| self.gen_type(ty))
83                    .collect::<Result<_, _>>()?,
84            )),
85            ast::ty::Type::Sum(types) => self.with_meta(Type::Sum(
86                types
87                    .iter()
88                    .map(|ty| self.gen_type(ty))
89                    .collect::<Result<_, _>>()?,
90            )),
91            ast::ty::Type::Function { parameters, body } => {
92                let span = self.pop_span().unwrap();
93                parameters
94                    .iter()
95                    .map(|parameter| self.gen_type(parameter))
96                    .collect::<Result<Vec<_>, _>>()?
97                    .into_iter()
98                    .try_rfold(self.gen_type(body)?, |body, parameter| {
99                        self.push_span(span.clone());
100                        Ok(self.with_meta(Type::Function {
101                            parameter: Box::new(parameter),
102                            body: Box::new(body),
103                        }))
104                    })?
105            }
106            ast::ty::Type::Array(ty) => self.with_meta(Type::Array(Box::new(self.gen_type(ty)?))),
107            ast::ty::Type::Set(ty) => self.with_meta(Type::Set(Box::new(self.gen_type(ty)?))),
108            ast::ty::Type::Let { variable, body } => self.with_meta(Type::Let {
109                variable: self.get_id_of(variable.clone()),
110                body: Box::new(self.gen_type(body)?),
111            }),
112            ast::ty::Type::BoundedVariable { bound, identifier } => {
113                self.with_meta(Type::BoundedVariable {
114                    bound: Box::new(self.gen_type(bound)?),
115                    identifier: identifier.clone(),
116                })
117            }
118            ast::ty::Type::Brand { brand, item } => {
119                if self.brands.borrow().contains(brand) {
120                    self.with_meta(Type::Brand {
121                        brand: brand.clone(),
122                        item: Box::new(self.gen_type(item)?),
123                    })
124                } else {
125                    self.with_meta(Type::Label {
126                        label: brand.clone(),
127                        item: Box::new(self.gen_type(item)?),
128                    })
129                }
130            }
131            ast::ty::Type::Attribute { attr, ty } => {
132                self.pop_span();
133                let mut ret = self.gen_type(ty)?;
134                let attr = self.gen(attr)?.value;
135                ret.meta.attrs.push(attr);
136                self.attrs
137                    .borrow_mut()
138                    .insert(ret.meta.id, ret.meta.attrs.clone());
139                ret
140            }
141            ast::ty::Type::Comment { item, .. } => self.gen_type(item)?,
142        };
143        Ok(with_meta)
144    }
145
146    pub fn gen(&self, ast: &Spanned<ast::expr::Expr>) -> Result<WithMeta<Expr>, HirGenError> {
147        let (expr, span) = ast;
148        self.push_span(span.clone());
149
150        let with_meta = match expr {
151            ast::expr::Expr::Literal(literal) => self.with_meta(Expr::Literal(match literal {
152                ast::expr::Literal::String(value) => Literal::String(value.clone()),
153                ast::expr::Literal::Int(value) => Literal::Int(*value),
154                ast::expr::Literal::Rational(a, b) => Literal::Rational(*a, *b),
155                ast::expr::Literal::Float(value) => Literal::Float(*value),
156            })),
157            ast::expr::Expr::Hole => self.with_meta(Expr::Literal(Literal::Hole)),
158            ast::expr::Expr::Let {
159                ty: variable,
160                definition,
161                body: expression,
162            } => self.with_meta(Expr::Let {
163                ty: self.gen_type(variable)?,
164                definition: Box::new(self.gen(definition)?),
165                expression: Box::new(self.gen(expression)?),
166            }),
167            ast::expr::Expr::Perform { input, output } => self.with_meta(Expr::Perform {
168                input: Box::new(self.gen(input)?),
169                output: self.gen_type(output)?,
170            }),
171            ast::expr::Expr::Continue { input, output } => self.with_meta(Expr::Continue {
172                input: Box::new(self.gen(input)?),
173                output: output
174                    .as_ref()
175                    .map(|output| self.gen_type(output))
176                    .transpose()?,
177            }),
178            ast::expr::Expr::Handle { handlers, expr } => self.with_meta(Expr::Handle {
179                handlers: handlers
180                    .iter()
181                    .map(
182                        |ast::expr::Handler {
183                             input,
184                             output,
185                             handler,
186                         }| {
187                            Ok(Handler {
188                                input: self.gen_type(input)?,
189                                output: self.gen_type(output)?,
190                                handler: self.gen(handler)?,
191                            })
192                        },
193                    )
194                    .collect::<Result<Vec<_>, _>>()?,
195                expr: Box::new(self.gen(expr)?),
196            }),
197            ast::expr::Expr::Apply {
198                function,
199                link_name,
200                arguments,
201            } => self.with_meta(Expr::Apply {
202                function: self.gen_type(function)?,
203                link_name: link_name.clone(),
204                arguments: arguments
205                    .iter()
206                    .map(|argument| self.gen(argument))
207                    .collect::<Result<Vec<_>, _>>()?,
208            }),
209            ast::expr::Expr::Product(items) => self.with_meta(Expr::Product(
210                items
211                    .iter()
212                    .map(|item| self.gen(item))
213                    .collect::<Result<_, _>>()?,
214            )),
215            ast::expr::Expr::Typed { ty, item: expr } => self.with_meta(Expr::Typed {
216                ty: self.gen_type(ty)?,
217                item: Box::new(self.gen(expr)?),
218            }),
219            ast::expr::Expr::Function { parameters, body } => {
220                let span = self.pop_span().unwrap();
221                parameters
222                    .iter()
223                    .map(|parameter| self.gen_type(parameter))
224                    .collect::<Result<Vec<_>, _>>()?
225                    .into_iter()
226                    .try_rfold(self.gen(body)?, |body, parameter| {
227                        self.push_span(span.clone());
228                        Ok(self.with_meta(Expr::Function {
229                            parameter,
230                            body: Box::new(body),
231                        }))
232                    })?
233            }
234            ast::expr::Expr::Array(items) => self.with_meta(Expr::Array(
235                items
236                    .iter()
237                    .map(|item| self.gen(item))
238                    .collect::<Result<_, _>>()?,
239            )),
240            ast::expr::Expr::Set(items) => self.with_meta(Expr::Set(
241                items
242                    .iter()
243                    .map(|item| self.gen(item))
244                    .collect::<Result<_, _>>()?,
245            )),
246            ast::expr::Expr::Include(file) => {
247                let InFile { id, expr } = self.included.get(file).unwrap();
248                self.push_file_id(*id);
249                let ret = self.gen(expr)?;
250                self.pop_file_id();
251                ret
252            }
253            ast::expr::Expr::Import { ty: _, uuid: _ } => todo!(),
254            ast::expr::Expr::Export { ty: _ } => todo!(),
255            ast::expr::Expr::Attribute { attr, item: expr } => {
256                self.pop_span();
257                let mut ret = self.gen(expr)?;
258                let attr = self.gen(attr)?.value;
259                ret.meta.attrs.push(attr);
260                self.attrs
261                    .borrow_mut()
262                    .insert(ret.meta.id, ret.meta.attrs.clone());
263                ret
264            }
265            ast::expr::Expr::Brand { brands, item: expr } => {
266                brands.iter().for_each(|brand| {
267                    self.brands.borrow_mut().insert(brand.clone());
268                });
269                self.gen(expr)?
270            }
271            ast::expr::Expr::Match { of, cases } => self.with_meta(Expr::Match {
272                of: Box::new(self.gen(of)?),
273                cases: cases
274                    .iter()
275                    .map(|ast::expr::MatchCase { ty, expr }| {
276                        Ok(MatchCase {
277                            ty: self.gen_type(ty)?,
278                            expr: self.gen(expr)?,
279                        })
280                    })
281                    .collect::<Result<Vec<_>, _>>()?,
282            }),
283            ast::expr::Expr::Label { label, item: expr } => {
284                if self.brands.borrow().contains(label) {
285                    self.with_meta(Expr::Brand {
286                        brand: label.clone(),
287                        item: Box::new(self.gen(expr)?),
288                    })
289                } else {
290                    self.with_meta(Expr::Label {
291                        label: label.clone(),
292                        item: Box::new(self.gen(expr)?),
293                    })
294                }
295            }
296            ast::expr::Expr::NewType { ident, ty, expr } => {
297                let ty = self.gen_type(ty)?.value;
298                self.type_aliases.borrow_mut().insert(ident.clone(), ty);
299                self.gen(expr)?
300            }
301            ast::expr::Expr::Comment { item, .. } => self.gen(item)?,
302            ast::expr::Expr::Card { uuid, .. } => {
303                return Err(HirGenError::UnexpectedCard { ident: *uuid });
304            }
305        };
306        Ok(with_meta)
307    }
308
309    pub(crate) fn push_span(&self, span: Span) {
310        self.next_span.borrow_mut().push(span);
311    }
312
313    pub(crate) fn pop_span(&self) -> Option<Span> {
314        self.next_span.borrow_mut().pop()
315    }
316
317    fn get_id_of(&self, ident: String) -> usize {
318        *self
319            .variables
320            .borrow_mut()
321            .entry(ident)
322            .or_insert_with(|| self.next_id())
323    }
324
325    pub fn next_id(&self) -> Id {
326        let id = *self.next_id.borrow();
327        *self.next_id.borrow_mut() += 1;
328        id
329    }
330
331    fn with_meta<T: std::fmt::Debug>(&self, value: T) -> WithMeta<T> {
332        WithMeta {
333            meta: Meta {
334                attrs: vec![],
335                id: self.next_id(),
336                file_id: *self.file_stack.borrow().last().unwrap(),
337                span: self.pop_span().unwrap(),
338            },
339            value,
340        }
341    }
342}
343
344#[cfg(test)]
345mod tests {
346    use hir::{
347        meta::{dummy_meta, Meta},
348        ty::Type,
349    };
350    use pretty_assertions::assert_eq;
351
352    use super::*;
353
354    fn parse(input: &str) -> Spanned<ast::expr::Expr> {
355        parser::parse(lexer::scan(input).unwrap()).unwrap()
356    }
357    fn remove_meta_ty(ty: WithMeta<Type>) -> WithMeta<Type> {
358        let value = match ty.value {
359            Type::Number => ty.value,
360            Type::String => ty.value,
361            Type::Trait(_) => todo!(),
362            Type::Effectful { ty, effects } => Type::Effectful {
363                ty: Box::new(remove_meta_ty(*ty)),
364                effects,
365            },
366            Type::Infer => ty.value,
367            Type::This => ty.value,
368            Type::Product(types) => Type::Product(types.into_iter().map(remove_meta_ty).collect()),
369            Type::Sum(types) => Type::Sum(types.into_iter().map(remove_meta_ty).collect()),
370            Type::Function { parameter, body } => Type::Function {
371                parameter: Box::new(remove_meta_ty(*parameter)),
372                body: Box::new(remove_meta_ty(*body)),
373            },
374            Type::Array(ty) => Type::Array(Box::new(remove_meta_ty(*ty))),
375            Type::Set(ty) => Type::Set(Box::new(remove_meta_ty(*ty))),
376            Type::Let { variable, body } => Type::Let {
377                variable,
378                body: Box::new(remove_meta_ty(*body)),
379            },
380            Type::Variable(_) => ty.value,
381            Type::BoundedVariable { bound, identifier } => Type::BoundedVariable {
382                bound: Box::new(remove_meta_ty(*bound)),
383                identifier,
384            },
385            Type::Brand { brand, item } => Type::Brand {
386                brand,
387                item: Box::new(remove_meta_ty(*item)),
388            },
389            Type::Label { label, item } => Type::Label {
390                label,
391                item: Box::new(remove_meta_ty(*item)),
392            },
393        };
394        dummy_meta(value)
395    }
396    fn remove_meta(expr: WithMeta<Expr>) -> WithMeta<Expr> {
397        let value = match expr.value {
398            Expr::Literal(_) => expr.value,
399            Expr::Let {
400                ty,
401                definition,
402                expression,
403            } => Expr::Let {
404                ty: remove_meta_ty(ty),
405                definition: Box::new(remove_meta(*definition)),
406                expression: Box::new(remove_meta(*expression)),
407            },
408            Expr::Perform { input, output } => Expr::Perform {
409                input: Box::new(remove_meta(*input)),
410                output: remove_meta_ty(output),
411            },
412            Expr::Continue { input, output } => Expr::Continue {
413                input: Box::new(remove_meta(*input)),
414                output: output.map(remove_meta_ty),
415            },
416            Expr::Handle { handlers, expr } => Expr::Handle {
417                handlers: handlers
418                    .into_iter()
419                    .map(
420                        |Handler {
421                             input,
422                             output,
423                             handler,
424                         }| Handler {
425                            input: remove_meta_ty(input),
426                            output: remove_meta_ty(output),
427                            handler: remove_meta(handler),
428                        },
429                    )
430                    .collect(),
431                expr: Box::new(remove_meta(*expr)),
432            },
433            Expr::Apply {
434                function,
435                link_name,
436                arguments,
437            } => Expr::Apply {
438                function: remove_meta_ty(function),
439                link_name,
440                arguments: arguments.into_iter().map(remove_meta).collect(),
441            },
442            Expr::Product(exprs) => Expr::Product(exprs.into_iter().map(remove_meta).collect()),
443            Expr::Typed { ty, item: expr } => Expr::Typed {
444                ty: remove_meta_ty(ty),
445                item: Box::new(remove_meta(*expr)),
446            },
447            Expr::Function { parameter, body } => Expr::Function {
448                parameter: remove_meta_ty(parameter),
449                body: Box::new(remove_meta(*body)),
450            },
451            Expr::Array(exprs) => Expr::Array(exprs.into_iter().map(remove_meta).collect()),
452            Expr::Set(exprs) => Expr::Set(exprs.into_iter().map(remove_meta).collect()),
453            Expr::Match { of, cases } => Expr::Match {
454                of: Box::new(remove_meta(*of)),
455                cases: cases
456                    .into_iter()
457                    .map(|MatchCase { ty, expr }| MatchCase {
458                        ty: remove_meta_ty(ty),
459                        expr: remove_meta(expr),
460                    })
461                    .collect(),
462            },
463            Expr::Label { label, item: body } => Expr::Label {
464                label,
465                item: Box::new(remove_meta(*body)),
466            },
467            Expr::Brand { brand, item: body } => Expr::Brand {
468                brand,
469                item: Box::new(remove_meta(*body)),
470            },
471        };
472        dummy_meta(value)
473    }
474
475    #[test]
476    fn test() {
477        let gen = HirGen::default();
478        gen.push_file_id(FileId(0));
479        assert_eq!(
480            gen.gen(&(
481                ast::expr::Expr::Apply {
482                    function: (ast::ty::Type::Number, 3..10),
483                    link_name: None,
484                    arguments: vec![(
485                        ast::expr::Expr::Attribute {
486                            attr: Box::new((
487                                ast::expr::Expr::Literal(ast::expr::Literal::Int(1)),
488                                24..25
489                            )),
490                            item: Box::new((
491                                ast::expr::Expr::Attribute {
492                                    attr: Box::new((
493                                        ast::expr::Expr::Literal(ast::expr::Literal::Int(2)),
494                                        24..25
495                                    )),
496                                    item: Box::new((ast::expr::Expr::Hole, 26..27)),
497                                },
498                                25..26
499                            )),
500                        },
501                        24..27
502                    )],
503                },
504                0..27
505            ),),
506            Ok(WithMeta {
507                meta: Meta {
508                    attrs: vec![],
509                    id: 4,
510                    file_id: FileId(0),
511                    span: 0..27
512                },
513                value: Expr::Apply {
514                    function: WithMeta {
515                        meta: Meta {
516                            attrs: vec![],
517                            id: 0,
518                            file_id: FileId(0),
519                            span: 3..10
520                        },
521                        value: Type::Number
522                    },
523                    link_name: Default::default(),
524                    arguments: vec![WithMeta {
525                        meta: Meta {
526                            attrs: vec![
527                                Expr::Literal(Literal::Int(2)),
528                                Expr::Literal(Literal::Int(1))
529                            ],
530                            id: 1,
531                            file_id: FileId(0),
532                            span: 26..27
533                        },
534                        value: Expr::Literal(Literal::Hole)
535                    }],
536                },
537            })
538        );
539
540        assert_eq!(
541            gen.attrs.borrow_mut().get(&1),
542            Some(&vec![
543                Expr::Literal(Literal::Int(2)),
544                Expr::Literal(Literal::Int(1))
545            ])
546        );
547    }
548
549    #[test]
550    fn label_and_brand() {
551        let expr = parse(
552            r#"
553        $ & @brand 'number ~
554        'brand brand ~
555        $ & @brand 'number ~
556        & @label 'number
557        "#,
558        );
559
560        let gen = HirGen::default();
561        gen.push_file_id(FileId(0));
562        assert_eq!(
563            remove_meta(gen.gen(&expr).unwrap()),
564            dummy_meta(Expr::Let {
565                ty: dummy_meta(Type::Infer),
566                definition: Box::new(dummy_meta(Expr::Apply {
567                    function: dummy_meta(Type::Label {
568                        label: "brand".into(),
569                        item: Box::new(dummy_meta(Type::Number)),
570                    }),
571                    link_name: Default::default(),
572                    arguments: vec![],
573                })),
574                expression: Box::new(dummy_meta(Expr::Let {
575                    ty: dummy_meta(Type::Infer),
576                    definition: Box::new(dummy_meta(Expr::Apply {
577                        function: dummy_meta(Type::Brand {
578                            brand: "brand".into(),
579                            item: Box::new(dummy_meta(Type::Number)),
580                        }),
581                        link_name: Default::default(),
582                        arguments: vec![],
583                    })),
584                    expression: Box::new(dummy_meta(Expr::Apply {
585                        function: dummy_meta(Type::Label {
586                            label: "label".into(),
587                            item: Box::new(dummy_meta(Type::Number)),
588                        }),
589                        link_name: Default::default(),
590                        arguments: vec![],
591                    }))
592                }))
593            })
594        )
595    }
596}