kodept_interpret/
semantic_analyzer.rs

1use tracing::debug;
2
3use kodept_ast::{
4    AbstFnDecl, BodyFnDecl, EnumDecl, ModDecl,
5    StructDecl, TyParam, TyName, NonTyParam, VarDecl,
6};
7use kodept_ast::graph::{ChangeSet, AnyNode};
8use kodept_ast::traits::Identifiable;
9use kodept_ast::utils::Execution;
10use kodept_ast::visit_side::{VisitGuard, VisitSide};
11use kodept_inference::r#type::MonomorphicType::Constant;
12use kodept_macros::Macro;
13use kodept_macros::traits::Context;
14
15use crate::scope::{ScopeError, ScopeTree};
16
17pub struct ScopeAnalyzer(ScopeTree);
18
19impl Default for ScopeAnalyzer {
20    fn default() -> Self {
21        Self(ScopeTree::new())
22    }
23}
24
25impl ScopeAnalyzer {
26    pub fn new() -> Self {
27        Self::default()
28    }
29
30    pub fn into_inner(self) -> ScopeTree {
31        self.0
32    }
33
34    fn divide_by_scopes(
35        &mut self,
36        node: &AnyNode,
37        side: VisitSide,
38    ) -> Result<(), ScopeError> {
39        let divide = match node {
40            AnyNode::ModDecl(ModDecl { name, .. }) => Some(Some(name)),
41            AnyNode::StructDecl(StructDecl { name, .. }) => Some(Some(name)),
42            AnyNode::EnumDecl(EnumDecl { name, .. }) => Some(Some(name)),
43            AnyNode::AbstFnDecl(AbstFnDecl { name, .. }) => {
44                Some(Some(name))
45            }
46            AnyNode::BodyFnDecl(BodyFnDecl { name, .. }) => {
47                Some(Some(name))
48            }
49            AnyNode::FileDecl(_) => Some(None),
50            AnyNode::Exprs(_) => Some(None),
51            AnyNode::Lambda(_) => Some(None),
52            AnyNode::IfExpr(_) => Some(None),
53            AnyNode::ElifExpr(_) => Some(None),
54            AnyNode::ElseExpr(_) => Some(None),
55            _ => None,
56        };
57
58        if let Some(name) = divide {
59            if side == VisitSide::Entering {
60                self.0.push_scope(node, name)
61            }
62            if side == VisitSide::Exiting {
63                self.0.pop_scope()?
64            }
65        }
66        Ok(())
67    }
68}
69
70impl Macro for ScopeAnalyzer {
71    type Error = ScopeError;
72    type Node = AnyNode;
73
74    fn transform(
75        &mut self,
76        guard: VisitGuard<Self::Node>,
77        context: &mut impl Context,
78    ) -> Execution<Self::Error, ChangeSet> {
79        let (node, side) = guard.allow_all();
80
81        if side == VisitSide::Exiting {
82            debug!("{:#?}", self.0);
83        }
84
85        self.divide_by_scopes(&node, side)?;
86
87        if !matches!(side, VisitSide::Exiting | VisitSide::Leaf) {
88            return Execution::Skipped;
89        }
90
91        let Ok(scope) = self.0.current_mut() else {
92            return Execution::Skipped;
93        };
94        let Some(tree) = context.tree().upgrade() else {
95            return Execution::Skipped;
96        };
97        match &*node {
98            AnyNode::StructDecl(StructDecl { name, .. }) => {
99                scope.insert_type(name, Constant(name.clone()))?;
100            }
101            AnyNode::TyParam(TyParam { name, .. }) => {
102                scope.insert_var(node.get_id(), name)?;
103            }
104            AnyNode::NonTyParam(NonTyParam { name, .. }) => {
105                scope.insert_var(node.get_id(), name)?;
106            }
107            AnyNode::TyName(TyName { name, .. }) => {
108                if let Some(AnyNode::EnumDecl(_)) = tree.parent_of(node.get_id(), node.token()) {
109                    scope.insert_type(name, Constant(name.clone()))?;
110                }
111            }
112            AnyNode::EnumDecl(EnumDecl { name, .. }) => {
113                scope.insert_type(name, Constant(name.clone()))?;
114            }
115            AnyNode::VarDecl(VarDecl { name, .. }) => scope.insert_var(node.get_id(), name)?,
116            AnyNode::BodyFnDecl(BodyFnDecl { name, .. }) => {
117                scope.insert_var(node.get_id(), name)?;
118            }
119            AnyNode::AbstFnDecl(AbstFnDecl { name, .. }) => {
120                scope.insert_var(node.get_id(), name)?
121            }
122            _ => {}
123        }
124
125        Execution::Completed(ChangeSet::new())
126    }
127}