scrapile/lang/typed/
root.rs

1use std::collections::HashMap;
2use crate::lang::{error::typed::Error, parser::root::Roots};
3use super::{block::{self, TBlock}, function::FuncSignature, symbol_table::{FuncTable, TypeTable, VarTable, VarTableEntry}};
4
5/// A type annotated representation of the entire project with all the roots evaluated statically
6#[derive(Debug)]
7pub struct Project {
8    /// The main block / procedure
9    pub main: TBlock,
10    
11    /// Additional user-defined procedures
12    pub procedures: Vec<(String, TBlock)>,
13}
14
15/// Wraps the root of the project in types and returns a single, safe and valid project root
16pub fn wrap_root(roots: &Roots) -> Result<Project, Error> {
17    let type_table = TypeTable(HashMap::new());
18    let mut func_table = FuncTable(HashMap::new());
19
20    // iterate through the functions and gather their signatures
21    for func in roots.funcs.iter() {
22        let signature = (FuncSignature {
23            params: func.0.params.iter()
24                .map(|((ident, ptype), span)| ((ident.clone(), ptype.clone()), span.clone()))
25                .collect::<Vec<_>>(),
26            retrn_type: func.0.retrn_type.clone(),
27        }, func.1.clone());
28
29        // insert and also check for duplicate function definitions
30        if let Some(old) = func_table.0.insert(func.0.ident.clone(), signature) {
31            return Err(Error::MultipleFunc {
32                first_span: old.1,
33                additional_span: func.1.clone(),
34            })
35        }
36    }
37
38    // make sure there's one and only one main root, otherwise throw an error
39    let Some(main) = roots.main.get(0)
40    else {
41        return Err(Error::NoMain)        
42    };
43    if let Some(extra) = roots.main.get(1) {
44        return Err(Error::MultipleMain {
45            first_span: main.1.clone(),
46            additional_span: extra.1.clone(),
47        });
48    }
49
50    // wrap the main block in types
51    let main = block::wrap_block(
52        main.0.clone(),
53        &type_table,
54        &func_table,
55        VarTable::new("$root".to_string()),
56    )?.0;
57
58    // wrap the rest of the function definitions in types
59    let mut procedures = Vec::new();
60    for func in roots.funcs.iter() {
61        let mut var_table = VarTable::new(format!("$func${}", func.0.ident));
62
63        // insert the parameters
64        for param in func.0.params.iter() {
65            var_table.insert(param.0.0.clone(), VarTableEntry {
66                var_type: param.0.1.clone(),
67                mutable: false,
68                span: param.1.clone(),
69            });
70        }
71
72        // wrap the procedures
73        let wrapped = block::wrap_block(func.0.body.0.clone(), &type_table, &func_table, var_table)?;
74
75        // make sure the body's return value is of the right type
76        if wrapped.1 != func.0.retrn_type.0 {
77            return Err(Error::RetrnTypeMismatch {
78                span: wrapped.0.tail.map(|((_, span), _)| span.clone()).unwrap_or_else(|| func.0.body.1.clone()),
79                type_span: func.0.retrn_type.1.clone(),
80                expr_type: wrapped.1.clone(),
81                retrn_type: func.0.retrn_type.0.clone(),
82            })
83        }
84
85        // push the wrapped block
86        procedures.push((func.0.ident.clone(), wrapped.0));
87    }
88
89    Ok(Project {
90        main,
91        procedures,
92    })
93}