lunify/function/
constant.rs

1use crate::number::Number;
2
3#[derive(Debug, PartialEq)]
4pub(crate) enum Constant {
5    Nil,
6    Boolean(bool),
7    Number(Number),
8    String(String),
9}
10
11pub(super) struct ConstantManager<'a> {
12    pub(super) constants: &'a mut Vec<Constant>,
13}
14
15impl<'a> ConstantManager<'a> {
16    pub(super) fn create_unique(&mut self, program_counter: usize) -> u64 {
17        let constant_index = self.constants.len() as u64;
18        let mut index = 0;
19
20        let constant = loop {
21            let constant_name = format!("__%lunify%__temp{program_counter}_{index}\0");
22            let constant = Constant::String(constant_name);
23
24            if !self.constants.contains(&constant) {
25                break constant;
26            }
27
28            index += 1;
29        };
30
31        self.constants.push(constant);
32        constant_index
33    }
34
35    pub(super) fn constant_for_str(&mut self, constant_str: &'static str) -> u64 {
36        let zero_terminated = format!("{constant_str}\0");
37
38        // If the constant already exists we don't need to add it again.
39        let matches = |constant: &_| matches!(constant, Constant::String(string) if string == zero_terminated.as_str());
40        if let Some(index) = self.constants.iter().position(matches) {
41            return index as u64;
42        }
43
44        let constant_index = self.constants.len() as u64;
45        self.constants.push(Constant::String(zero_terminated));
46        constant_index
47    }
48
49    pub(super) fn constant_nil(&mut self) -> u64 {
50        // If the constant already exists we don't need to add it again.
51        let matches = |constant: &_| matches!(constant, Constant::Nil);
52        if let Some(index) = self.constants.iter().position(matches) {
53            return index as u64;
54        }
55
56        let constant_index = self.constants.len() as u64;
57        self.constants.push(Constant::Nil);
58        constant_index
59    }
60}
61
62#[cfg(test)]
63mod tests {
64    use super::{Constant, ConstantManager};
65
66    #[test]
67    fn create_unique() {
68        let mut constants = Vec::new();
69        let mut constant_manager = ConstantManager { constants: &mut constants };
70
71        assert_eq!(constant_manager.create_unique(9), 0);
72        assert_eq!(&constants[0], &Constant::String("__%lunify%__temp9_0\0".to_owned()));
73    }
74
75    #[test]
76    fn create_unique_twice() {
77        let mut constants = vec![Constant::String("__%lunify%__temp9_0\0".to_owned())];
78        let mut constant_manager = ConstantManager { constants: &mut constants };
79
80        assert_eq!(constant_manager.create_unique(9), 1);
81        assert_eq!(&constants[1], &Constant::String("__%lunify%__temp9_1\0".to_owned()));
82    }
83
84    #[test]
85    fn constant_for_str() {
86        let mut constants = vec![Constant::String("constant".to_owned())];
87        let mut constant_manager = ConstantManager { constants: &mut constants };
88
89        assert_eq!(constant_manager.constant_for_str("test"), 1);
90        assert_eq!(&constants[1], &Constant::String("test\0".to_owned()));
91    }
92
93    #[test]
94    fn constant_for_str_duplicate() {
95        let mut constants = vec![Constant::String("test\0".to_owned()), Constant::String("constant".to_owned())];
96        let mut constant_manager = ConstantManager { constants: &mut constants };
97
98        assert_eq!(constant_manager.constant_for_str("test"), 0);
99    }
100
101    #[test]
102    fn constant_nil() {
103        let mut constants = vec![Constant::String("constant".to_owned())];
104        let mut constant_manager = ConstantManager { constants: &mut constants };
105
106        assert_eq!(constant_manager.constant_nil(), 1);
107        assert_eq!(&constants[1], &Constant::Nil);
108    }
109
110    #[test]
111    fn constant_nil_duplicate() {
112        let mut constants = vec![Constant::Nil, Constant::String("constant".to_owned())];
113        let mut constant_manager = ConstantManager { constants: &mut constants };
114
115        assert_eq!(constant_manager.constant_nil(), 0);
116    }
117}