nessa/
serialization.rs

1use std::{fs, path::Path};
2
3use crate::{cache::NessaCache, compilation::{CompiledNessaExpr, NessaError, NessaInstruction}, config::{ImportMap, InnerDepGraph, NessaModule}, context::{standard_ctx, NessaContext, NUM_STD_BINOPS, NUM_STD_FNS, NUM_STD_INTS, NUM_STD_INT_IMPL, NUM_STD_MACROS, NUM_STD_NARYOPS, NUM_STD_TYPES, NUM_STD_UNOPS}, execution::ExecutionInfo, functions::Function, interfaces::{Interface, InterfaceImpl}, macros::NessaMacro, operations::Operator, parser::NessaExpr, types::TypeTemplate};
4
5use serde::{Serialize, Deserialize};
6use bitcode;
7
8#[derive(Clone, Serialize, Deserialize)]
9pub struct ReducedNessaModule {
10    pub name: String,
11    pub hash: String,
12
13    pub code: Vec<NessaExpr>,
14    pub source: Vec<String>,
15    pub imports: ImportMap,
16    pub inner_dependencies: InnerDepGraph,
17
18    pub type_templates: Vec<TypeTemplate>, 
19    pub interfaces: Vec<Interface>,
20    pub interface_impls: Vec<InterfaceImpl>,
21
22    pub unary_ops: Vec<Operator>,
23    pub binary_ops: Vec<Operator>,
24    pub nary_ops: Vec<Operator>,
25
26    pub functions: Vec<Function>,
27
28    pub macros: Vec<NessaMacro>,
29
30    pub cache: NessaCache
31}
32
33impl NessaModule {
34    pub fn get_reduced_module(&self) -> ReducedNessaModule {
35        let reduced_types = self.ctx.type_templates[*NUM_STD_TYPES.lock().unwrap().borrow()..].to_vec();
36        let reduced_ints = self.ctx.interfaces[*NUM_STD_INTS.lock().unwrap().borrow()..].to_vec();
37        let reduced_int_impls = self.ctx.interface_impls[*NUM_STD_INT_IMPL.lock().unwrap().borrow()..].to_vec();
38        let reduced_macros = self.ctx.macros[*NUM_STD_MACROS.lock().unwrap().borrow()..].to_vec();
39
40        let func_map = NUM_STD_FNS.lock().unwrap();
41        let unop_map = NUM_STD_UNOPS.lock().unwrap();
42        let binop_map = NUM_STD_BINOPS.lock().unwrap();
43        let naryop_map = NUM_STD_NARYOPS.lock().unwrap();
44
45        let reduced_functions = self.ctx.functions.iter().map(|f| {
46            let mut f_cpy = f.clone();
47            
48            if let Some(inner) = func_map.borrow().get(&f_cpy.id) {
49                f_cpy.overloads = f_cpy.overloads[*inner..].to_vec();
50            }
51
52            f_cpy
53        })
54        .collect();
55
56        let reduced_unops = self.ctx.unary_ops.iter().filter_map(|o| {
57            let mut o_cpy = o.clone();
58            
59            if let Operator::Unary { id, operations, .. } = &mut o_cpy {
60                if let Some(inner) = unop_map.borrow().get(id) {
61                    *operations = operations[*inner..].to_vec();
62                }
63
64                return Some(o_cpy);
65            }
66
67            unreachable!();
68        }).collect();
69
70        let reduced_binops = self.ctx.binary_ops.iter().filter_map(|o| {
71            let mut o_cpy = o.clone();
72            
73            if let Operator::Binary { id, operations, .. } = &mut o_cpy {
74                if let Some(inner) = binop_map.borrow().get(id) {
75                    *operations = operations[*inner..].to_vec();
76                }
77
78                return Some(o_cpy);
79            }
80
81            unreachable!();
82        }).collect();
83
84        let reduced_naryops = self.ctx.nary_ops.iter().filter_map(|o| {
85            let mut o_cpy = o.clone();
86            
87            if let Operator::Nary { id, operations, .. } = &mut o_cpy {
88                if let Some(inner) = naryop_map.borrow().get(id) {
89                    *operations = operations[*inner..].to_vec();
90                }
91
92                return Some(o_cpy);
93            }
94
95            unreachable!();
96        }).collect();
97
98        ReducedNessaModule {
99            name: self.name.clone(),
100            hash: self.hash.clone(),
101            code: self.code.clone(), 
102            source: self.source.clone(),
103            imports: self.imports.clone(),
104            inner_dependencies: self.inner_dependencies.clone(),
105            type_templates: reduced_types,
106            interfaces: reduced_ints,
107            interface_impls: reduced_int_impls,
108            unary_ops: reduced_unops,
109            binary_ops: reduced_binops,
110            nary_ops: reduced_naryops,
111            functions: reduced_functions,
112            macros: reduced_macros,
113            cache: self.ctx.cache.clone(),
114        }
115    }
116}
117
118impl ReducedNessaModule {
119    pub fn recover_module(mut self) -> NessaModule {
120        let mut std_ctx = standard_ctx();
121
122        // Reconstruct original context
123        std_ctx.cache = self.cache;
124
125        std_ctx.type_templates.append(&mut self.type_templates);
126        std_ctx.interfaces.append(&mut self.interfaces);
127        std_ctx.interface_impls.append(&mut self.interface_impls);
128        std_ctx.macros.append(&mut self.macros);
129
130        for mut f in self.functions {
131            if f.id < std_ctx.functions.len() {
132                std_ctx.functions[f.id].overloads.append(&mut f.overloads);
133
134            } else {
135                std_ctx.functions.push(f);
136            }
137        }
138
139        for mut o in self.unary_ops {
140            if let Operator::Unary { id, operations, .. } = &mut o {
141                if *id < std_ctx.unary_ops.len() {
142                    if let Operator::Unary { operations: operations_std, .. } = &mut std_ctx.unary_ops[*id] {
143                        operations_std.append(operations);
144                    }
145    
146                } else {
147                    std_ctx.unary_ops.push(o);
148                }
149
150            } else {
151                unreachable!();
152            }
153        }
154
155        for mut o in self.binary_ops {
156            if let Operator::Binary { id, operations, .. } = &mut o {
157                if *id < std_ctx.binary_ops.len() {
158                    if let Operator::Binary { operations: operations_std, .. } = &mut std_ctx.binary_ops[*id] {
159                        operations_std.append(operations);
160                    }
161    
162                } else {
163                    std_ctx.binary_ops.push(o);
164                }
165
166            } else {
167                unreachable!();
168            }
169        }
170
171        for mut o in self.nary_ops {
172            if let Operator::Nary { id, operations, .. } = &mut o {
173                if *id < std_ctx.nary_ops.len() {
174                    if let Operator::Nary { operations: operations_std, .. } = &mut std_ctx.nary_ops[*id] {
175                        operations_std.append(operations);
176                    }
177    
178                } else {
179                    std_ctx.nary_ops.push(o);
180                }
181
182            } else {
183                unreachable!();
184            }
185        }
186
187        NessaModule {
188            name: self.name.clone(),
189            hash: self.hash.clone(),
190            ctx: std_ctx,
191            code: self.code,
192            source: self.source,
193            imports: self.imports,
194            inner_dependencies: self.inner_dependencies,
195        }
196    }
197
198    pub fn deserialize(data: &[u8]) -> Self {
199        bitcode::deserialize(data).expect("Unable to deserialize module")
200    }
201
202    pub fn serialize(&self) -> Vec<u8> {
203        bitcode::serialize(self).expect("Unable to serialize module")
204    }
205
206    pub fn from_file(path: &Path) -> Self {
207        let data = fs::read(path).expect("Unable to read serialized module from file");
208        ReducedNessaModule::deserialize(&data)
209    }
210
211    pub fn write_to_file(&self, path: &Path) {
212        fs::write(path, self.serialize()).expect("Unable to write serialized module to file");
213    }
214}
215
216#[derive(Clone, Serialize, Deserialize)]
217pub struct CompiledNessaModule {
218    pub hash: String,
219    type_templates: Vec<TypeTemplate>,
220    interface_impls: Vec<InterfaceImpl>,
221    instructions: Vec<CompiledNessaExpr>
222}
223
224impl NessaContext {
225    pub fn get_serializable_module(&self, hash: String, instructions: &[NessaInstruction]) -> CompiledNessaModule {
226        return CompiledNessaModule {
227            hash, 
228            type_templates: self.type_templates[*NUM_STD_TYPES.lock().unwrap().borrow()..].to_vec(), 
229            interface_impls: self.interface_impls[*NUM_STD_INT_IMPL.lock().unwrap().borrow()..].to_vec(), 
230            instructions: instructions.iter().map(|i| i.instruction.clone()).collect()
231        };
232    }
233}
234
235impl CompiledNessaModule {
236    pub fn deserialize(data: &[u8]) -> Self {
237        bitcode::deserialize(data).expect("Unable to deserialize code")
238    }
239
240    pub fn serialize(&self) -> Vec<u8> {
241        bitcode::serialize(self).expect("Unable to serialize code")
242    }
243
244    pub fn from_file(path: &Path) -> Self {
245        let data = fs::read(path).expect("Unable to read serialized code from file");
246        CompiledNessaModule::deserialize(&data)
247    }
248
249    pub fn write_to_file(&self, path: &Path) {
250        fs::write(path, self.serialize()).expect("Unable to write serialized code to file");
251    }
252
253    pub fn execute<const DEBUG: bool>(&mut self, program_input: &[String]) -> Result<ExecutionInfo, NessaError> {
254        let mut ctx = standard_ctx();
255
256        ctx.type_templates.append(&mut self.type_templates);
257        ctx.interface_impls.append(&mut self.interface_impls);
258
259        ctx.program_input = program_input.to_vec();
260
261        ctx.execute_compiled_code::<DEBUG>(&self.instructions, &[])
262    }
263}