Skip to main content

runmat_core/session/
compile.rs

1use super::*;
2
3impl RunMatSession {
4    pub(crate) fn compile_input(
5        &mut self,
6        input: &str,
7    ) -> std::result::Result<PreparedExecution, RunError> {
8        let source_name = self.current_source_name().to_string();
9        let source_id = self.source_pool.intern(&source_name, input);
10        let ast = {
11            let _span = info_span!("runtime.parse").entered();
12            parse_with_options(input, ParserOptions::new(self.compat_mode))?
13        };
14        let lowering = {
15            let _span = info_span!("runtime.lower").entered();
16            runmat_hir::lower(
17                &ast,
18                &LoweringContext::new(&self.variable_names, &self.function_definitions),
19            )?
20        };
21        let existing_functions = self.convert_hir_functions_to_user_functions();
22        let mut bytecode = {
23            let _span = info_span!("runtime.compile.bytecode").entered();
24            runmat_vm::compile(&lowering.hir, &existing_functions)?
25        };
26        bytecode.source_id = Some(source_id);
27        let new_function_names: HashSet<String> = lowering.functions.keys().cloned().collect();
28        for (name, func) in bytecode.functions.iter_mut() {
29            if new_function_names.contains(name) {
30                func.source_id = Some(source_id);
31            }
32        }
33        Ok(PreparedExecution {
34            ast,
35            lowering,
36            bytecode,
37        })
38    }
39
40    pub(crate) fn populate_callstack(&self, error: &mut RuntimeError) {
41        if !error.context.call_stack.is_empty() || error.context.call_frames.is_empty() {
42            return;
43        }
44        let mut rendered = Vec::new();
45        if error.context.call_frames_elided > 0 {
46            rendered.push(format!(
47                "... {} frames elided ...",
48                error.context.call_frames_elided
49            ));
50        }
51        for frame in error.context.call_frames.iter().rev() {
52            let mut line = frame.function.clone();
53            if let (Some(source_id), Some((start, _end))) = (frame.source_id, frame.span) {
54                if let Some(source) = self.source_pool.get(SourceId(source_id)) {
55                    let (line_num, col) = line_col_from_offset(&source.text, start);
56                    line = format!("{} @ {}:{}:{}", frame.function, source.name, line_num, col);
57                }
58            }
59            rendered.push(line);
60        }
61        error.context.call_stack = rendered;
62    }
63
64    pub(crate) fn normalize_error_namespace(&self, error: &mut RuntimeError) {
65        let Some(identifier) = error.identifier.clone() else {
66            return;
67        };
68        let suffix = identifier
69            .split_once(':')
70            .map(|(_, suffix)| suffix)
71            .unwrap_or(identifier.as_str());
72        error.identifier = Some(format!("{}:{suffix}", self.error_namespace));
73    }
74
75    /// Compile the input and produce a fusion plan snapshot without executing.
76    pub fn compile_fusion_plan(
77        &mut self,
78        input: &str,
79    ) -> std::result::Result<Option<FusionPlanSnapshot>, RunError> {
80        let prepared = self.compile_input(input)?;
81        Ok(build_fusion_snapshot(
82            prepared.bytecode.accel_graph.as_ref(),
83            &prepared.bytecode.fusion_groups,
84        ))
85    }
86
87    pub(crate) fn prepare_variable_array_for_execution(
88        &mut self,
89        bytecode: &runmat_vm::Bytecode,
90        updated_var_mapping: &HashMap<String, usize>,
91        debug_trace: bool,
92    ) {
93        // Create a new variable array of the correct size
94        let max_var_id = updated_var_mapping.values().copied().max().unwrap_or(0);
95        let required_len = std::cmp::max(bytecode.var_count, max_var_id + 1);
96        let mut new_variable_array = vec![Value::Num(0.0); required_len];
97        if debug_trace {
98            debug!(
99                bytecode_var_count = bytecode.var_count,
100                required_len, max_var_id, "[repl] prepare variable array"
101            );
102        }
103
104        // Populate with existing values based on the variable mapping
105        for (var_name, &new_var_id) in updated_var_mapping {
106            if new_var_id < new_variable_array.len() {
107                if let Some(value) = self.workspace_values.get(var_name) {
108                    if debug_trace {
109                        debug!(
110                            var_name,
111                            var_id = new_var_id,
112                            ?value,
113                            "[repl] prepare set var"
114                        );
115                    }
116                    new_variable_array[new_var_id] = value.clone();
117                }
118            } else if debug_trace {
119                debug!(
120                    var_name,
121                    var_id = new_var_id,
122                    len = new_variable_array.len(),
123                    "[repl] prepare skipping var"
124                );
125            }
126        }
127
128        // Update our variable array and mapping
129        self.variable_array = new_variable_array;
130    }
131
132    /// Convert stored HIR function definitions to UserFunction format for compilation
133    fn convert_hir_functions_to_user_functions(&self) -> HashMap<String, runmat_vm::UserFunction> {
134        let mut user_functions = HashMap::new();
135
136        for (name, hir_stmt) in &self.function_definitions {
137            if let runmat_hir::HirStmt::Function {
138                name: func_name,
139                params,
140                outputs,
141                body,
142                has_varargin: _,
143                has_varargout: _,
144                ..
145            } = hir_stmt
146            {
147                // Use the existing HIR utilities to calculate variable count
148                let var_map =
149                    runmat_hir::remapping::create_complete_function_var_map(params, outputs, body);
150                let max_local_var = var_map.len();
151
152                let source_id = self.function_source_ids.get(name).copied();
153                if let Some(id) = source_id {
154                    if let Some(source) = self.source_pool.get(id) {
155                        let _ = (&source.name, &source.text);
156                    }
157                }
158                let user_func = runmat_vm::UserFunction {
159                    name: func_name.clone(),
160                    params: params.clone(),
161                    outputs: outputs.clone(),
162                    body: body.clone(),
163                    local_var_count: max_local_var,
164                    has_varargin: false,
165                    has_varargout: false,
166                    var_types: vec![Type::Unknown; max_local_var],
167                    source_id,
168                };
169                user_functions.insert(name.clone(), user_func);
170            }
171        }
172
173        user_functions
174    }
175}