Skip to main content

runmat_vm/compiler/
classes.rs

1//! Class lowering.
2
3use crate::compiler::core::Compiler;
4use crate::compiler::CompileError;
5use crate::instr::Instr;
6
7impl Compiler {
8    fn attr_access_from_str(s: &str) -> runmat_builtins::Access {
9        match s.to_ascii_lowercase().as_str() {
10            "private" => runmat_builtins::Access::Private,
11            _ => runmat_builtins::Access::Public,
12        }
13    }
14
15    pub(crate) fn parse_prop_attrs(
16        attrs: &Vec<runmat_parser::Attr>,
17    ) -> (bool, bool, String, String) {
18        let mut is_static = false;
19        let mut is_dependent = false;
20        let mut get_acc = runmat_builtins::Access::Public;
21        let mut set_acc = runmat_builtins::Access::Public;
22        for a in attrs {
23            if a.name.eq_ignore_ascii_case("Static") {
24                is_static = true;
25            }
26            if a.name.eq_ignore_ascii_case("Dependent") {
27                is_dependent = true;
28            }
29            if a.name.eq_ignore_ascii_case("Access") {
30                if let Some(v) = &a.value {
31                    let acc = Self::attr_access_from_str(v.trim_matches('\'').trim());
32                    get_acc = acc.clone();
33                    set_acc = acc;
34                }
35            }
36            if a.name.eq_ignore_ascii_case("GetAccess") {
37                if let Some(v) = &a.value {
38                    get_acc = Self::attr_access_from_str(v.trim_matches('\'').trim());
39                }
40            }
41            if a.name.eq_ignore_ascii_case("SetAccess") {
42                if let Some(v) = &a.value {
43                    set_acc = Self::attr_access_from_str(v.trim_matches('\'').trim());
44                }
45            }
46        }
47        let gs = match get_acc {
48            runmat_builtins::Access::Private => "private".to_string(),
49            _ => "public".to_string(),
50        };
51        let ss = match set_acc {
52            runmat_builtins::Access::Private => "private".to_string(),
53            _ => "public".to_string(),
54        };
55        (is_static, is_dependent, gs, ss)
56    }
57
58    pub(crate) fn parse_method_attrs(attrs: &Vec<runmat_parser::Attr>) -> (bool, String) {
59        let mut is_static = false;
60        let mut access = runmat_builtins::Access::Public;
61        for a in attrs {
62            if a.name.eq_ignore_ascii_case("Static") {
63                is_static = true;
64            }
65            if a.name.eq_ignore_ascii_case("Access") {
66                if let Some(v) = &a.value {
67                    access = Self::attr_access_from_str(v.trim_matches('\'').trim());
68                }
69            }
70        }
71        let acc_str = match access {
72            runmat_builtins::Access::Private => "private".to_string(),
73            _ => "public".to_string(),
74        };
75        (is_static, acc_str)
76    }
77
78    pub(crate) fn compile_class_def(
79        &mut self,
80        name: &str,
81        super_class: &Option<String>,
82        members: &[runmat_hir::HirClassMember],
83    ) -> Result<(), CompileError> {
84        let mut props: Vec<(String, bool, String, String)> = Vec::new();
85        let mut methods: Vec<(String, String, bool, String)> = Vec::new();
86        for m in members {
87            match m {
88                runmat_hir::HirClassMember::Properties { names, attributes } => {
89                    let (is_static, is_dependent, get_access, set_access) =
90                        Self::parse_prop_attrs(attributes);
91                    for n in names {
92                        let enc = if is_dependent {
93                            format!("@dep:{n}")
94                        } else {
95                            n.clone()
96                        };
97                        props.push((enc, is_static, get_access.clone(), set_access.clone()));
98                    }
99                }
100                runmat_hir::HirClassMember::Methods { body, attributes } => {
101                    let (is_static, access) = Self::parse_method_attrs(attributes);
102                    for s in body {
103                        if let runmat_hir::HirStmt::Function { name: mname, .. } = s {
104                            methods.push((mname.clone(), mname.clone(), is_static, access.clone()));
105                        }
106                    }
107                }
108                _ => {}
109            }
110        }
111        self.emit(Instr::RegisterClass {
112            name: name.to_string(),
113            super_class: super_class.clone(),
114            properties: props,
115            methods,
116        });
117        Ok(())
118    }
119}