datex_core/compiler/
context.rs

1use crate::collections::HashMap;
2use crate::core_compiler::value_compiler::append_instruction_code;
3use crate::core_compiler::value_compiler::append_value_container;
4use crate::global::instruction_codes::InstructionCode;
5use crate::runtime::execution::context::ExecutionMode;
6use crate::utils::buffers::append_u32;
7use crate::values::value_container::ValueContainer;
8use core::cmp::PartialEq;
9use itertools::Itertools;
10
11#[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Hash)]
12pub struct VirtualSlot {
13    pub level: u8, // parent scope level if exists, otherwise 0
14    // local slot address of scope with level
15    pub virtual_address: u32,
16}
17
18impl VirtualSlot {
19    pub fn local(virtual_address: u32) -> Self {
20        VirtualSlot {
21            level: 0,
22            virtual_address,
23        }
24    }
25    pub fn is_external(&self) -> bool {
26        self.level > 0
27    }
28
29    pub fn external(level: u8, virtual_address: u32) -> Self {
30        VirtualSlot {
31            level,
32            virtual_address,
33        }
34    }
35
36    pub fn downgrade(&self) -> Self {
37        VirtualSlot {
38            level: self.level + 1,
39            virtual_address: self.virtual_address,
40        }
41    }
42
43    pub fn upgrade(&self) -> Self {
44        if self.level > 0 {
45            VirtualSlot {
46                level: self.level - 1,
47                virtual_address: self.virtual_address,
48            }
49        } else {
50            core::panic!("Cannot upgrade a local slot");
51        }
52    }
53}
54
55/// compilation context, created for each compiler call, even if compiling a script for the same scope
56pub struct CompilationContext {
57    pub inserted_value_index: usize,
58    pub buffer: Vec<u8>,
59    pub inserted_values: Vec<ValueContainer>,
60    /// this flag is set to true if any non-static value is encountered
61    pub has_non_static_value: bool,
62    pub execution_mode: ExecutionMode,
63
64    // mapping for temporary scope slot resolution
65    slot_indices: HashMap<VirtualSlot, Vec<u32>>,
66}
67
68impl CompilationContext {
69    const MAX_INT_32: i64 = 2_147_483_647;
70    const MIN_INT_32: i64 = -2_147_483_648;
71
72    const MAX_INT_8: i64 = 127;
73    const MIN_INT_8: i64 = -128;
74
75    const MAX_INT_16: i64 = 32_767;
76    const MIN_INT_16: i64 = -32_768;
77
78    const MAX_UINT_16: i64 = 65_535;
79
80    const INT_8_BYTES: u8 = 1;
81    const INT_16_BYTES: u8 = 2;
82    const INT_32_BYTES: u8 = 4;
83    const INT_64_BYTES: u8 = 8;
84    const INT_128_BYTES: u8 = 16;
85
86    const FLOAT_32_BYTES: u8 = 4;
87    const FLOAT_64_BYTES: u8 = 8;
88
89    pub fn new(
90        buffer: Vec<u8>,
91        inserted_values: Vec<ValueContainer>,
92        execution_mode: ExecutionMode,
93    ) -> Self {
94        CompilationContext {
95            inserted_value_index: 0,
96            buffer,
97            inserted_values,
98            has_non_static_value: false,
99            slot_indices: HashMap::new(),
100            execution_mode,
101        }
102    }
103
104    pub fn buffer_index(&self) -> usize {
105        self.buffer.len()
106    }
107
108    fn insert_value_container(&mut self, value_container: &ValueContainer) {
109        append_value_container(&mut self.buffer, value_container);
110    }
111
112    pub fn external_slots(&self) -> Vec<VirtualSlot> {
113        self.slot_indices
114            .iter()
115            .filter(|(slot, _)| slot.is_external())
116            .sorted_by(|a, b| a.0.virtual_address.cmp(&b.0.virtual_address))
117            .map(|(slot, _)| *slot)
118            .collect()
119    }
120
121    /// Gets all slots for either local or external slots depending on the value of external
122    pub fn get_slot_byte_indices(
123        &self,
124        match_externals: bool,
125    ) -> Vec<Vec<u32>> {
126        self.slot_indices
127            .iter()
128            .filter(|(slot, _)| slot.is_external() == match_externals)
129            .sorted_by(|a, b| a.0.virtual_address.cmp(&b.0.virtual_address))
130            .map(|(_, indices)| indices.clone())
131            .collect()
132    }
133
134    pub fn remap_virtual_slots(&mut self) {
135        let mut slot_address = 0;
136
137        // parent slots
138        for byte_indices in self.get_slot_byte_indices(true) {
139            for byte_index in byte_indices {
140                self.set_u32_at_index(slot_address, byte_index as usize);
141            }
142            slot_address += 1;
143        }
144
145        // local slots
146        for byte_indices in self.get_slot_byte_indices(false) {
147            for byte_index in byte_indices {
148                self.set_u32_at_index(slot_address, byte_index as usize);
149            }
150            slot_address += 1;
151        }
152    }
153
154    // This method writes a placeholder value for the slot
155    // since the slot address is not known yet and just temporary.
156    pub fn insert_virtual_slot_address(&mut self, virtual_slot: VirtualSlot) {
157        let buffer_index = self.buffer_index() as u32;
158        if let Some(indices) = self.slot_indices.get_mut(&virtual_slot) {
159            indices.push(buffer_index);
160        } else {
161            self.slot_indices.insert(virtual_slot, vec![buffer_index]);
162        }
163        append_u32(&mut self.buffer, 0); // placeholder for the slot address
164    }
165
166    pub fn set_u32_at_index(&mut self, u32: u32, index: usize) {
167        self.buffer[index..index + CompilationContext::INT_32_BYTES as usize]
168            .copy_from_slice(&u32.to_le_bytes());
169    }
170
171    pub fn mark_has_non_static_value(&mut self) {
172        self.has_non_static_value = true;
173    }
174
175    pub fn append_instruction_code(&mut self, code: InstructionCode) {
176        append_instruction_code(&mut self.buffer, code);
177    }
178}