Skip to main content

litex/runtime/
runtime.rs

1use crate::prelude::*;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5pub struct Runtime {
6    pub module_manager: ModuleManager,
7    pub environment_stack: Vec<Box<Environment>>,
8    pub parsing_free_param_collection: FreeParamCollection,
9}
10
11impl Runtime {
12    pub fn new() -> Self {
13        let module_manager = ModuleManager::new_empty_module_manager(BUILTIN_CODE_PATH);
14        let new_environment = Box::new(Environment::new_empty_env());
15
16        Runtime {
17            module_manager,
18            environment_stack: vec![new_environment],
19            parsing_free_param_collection: FreeParamCollection::new(),
20        }
21    }
22
23    // Same empty runtime as `new`, then runs builtin definitions; panics if that fails.
24    pub fn new_with_builtin_code() -> Self {
25        let mut runtime = Self::new();
26        let (stmt_results, runtime_error) =
27            crate::pipeline::run_source_code(builtin_code().as_str(), &mut runtime);
28        let (ok, msg) = crate::pipeline::render_run_source_code_output(
29            &runtime,
30            &stmt_results,
31            &runtime_error,
32            true,
33        );
34        if !ok {
35            panic!("builtin code execution failed: {}", msg);
36        }
37        runtime
38    }
39}
40
41impl Runtime {
42    pub fn validate_name(
43        &mut self,
44        name: &str,
45        _current_line_file: LineFile,
46    ) -> Result<(), RuntimeError> {
47        if let Err(invalid_name_message) = is_valid_litex_name(name) {
48            return Err(ParseRuntimeError(RuntimeErrorStruct::new_with_just_msg(
49                invalid_name_message,
50            ))
51            .into());
52        }
53
54        Ok(())
55    }
56
57    pub fn validate_user_fn_param_names_for_parse(
58        &mut self,
59        names: &[String],
60        line_file: LineFile,
61    ) -> Result<(), RuntimeError> {
62        for name in names {
63            if let Err(e) = is_valid_litex_name(name) {
64                return Err(
65                    ParseRuntimeError(RuntimeErrorStruct::new_with_msg_and_line_file(
66                        e,
67                        line_file.clone(),
68                    ))
69                    .into(),
70                );
71            }
72        }
73        Ok(())
74    }
75
76    pub fn validate_names_and_insert_into_top_parsing_time_name_scope(
77        &mut self,
78        names: &Vec<String>,
79        line_file: LineFile,
80    ) -> Result<(), RuntimeError> {
81        for name in names {
82            self.validate_name_and_insert_into_top_parsing_time_name_scope(
83                name,
84                line_file.clone(),
85            )?;
86        }
87        Ok(())
88    }
89
90    /// Validates identifier syntax only; does not record bindings (see `run_in_local_parsing_time_name_scope`).
91    pub fn validate_name_and_insert_into_top_parsing_time_name_scope(
92        &mut self,
93        name: &str,
94        line_file: LineFile,
95    ) -> Result<(), RuntimeError> {
96        self.validate_name(name, line_file)
97    }
98}
99
100impl Runtime {
101    pub fn new_file_path_new_env_new_name_scope(&mut self, path: &str) {
102        let path_rc: Rc<str> = Rc::from(path);
103        self.module_manager.run_file_paths.push(path_rc.clone());
104        self.module_manager.current_file_index += 1;
105        self.module_manager.display_entry_rc = Some(path_rc);
106        self.module_manager.entry_path = path.to_string();
107        self.push_env();
108    }
109
110    /// After `new_file_path_new_env_new_name_scope`, point the current user entry slot at another
111    /// path without pushing more layers (pair with `clear_current_env_and_parse_name_scope`).
112    pub fn set_current_user_lit_file_path(&mut self, path: &str) {
113        let path_rc: Rc<str> = Rc::from(path);
114        let idx = self.module_manager.current_file_index;
115        debug_assert!(
116            idx > FILE_INDEX_FOR_BUILTIN,
117            "set_current_user_lit_file_path requires a prior new_file_path_new_env_new_name_scope"
118        );
119        if let Some(slot) = self.module_manager.run_file_paths.get_mut(idx) {
120            *slot = path_rc.clone();
121        }
122        self.module_manager.display_entry_rc = Some(path_rc);
123        self.module_manager.entry_path = path.to_string();
124    }
125}
126
127impl Runtime {
128    pub fn top_level_env(&mut self) -> &mut Environment {
129        let result = self.environment_stack.last_mut();
130        match result {
131            Some(environment) => environment,
132            None => unreachable!("no top level environment"),
133        }
134    }
135}
136
137impl Runtime {
138    fn push_env(&mut self) {
139        let new_env = Box::new(Environment::new_empty_env());
140        self.environment_stack.push(new_env);
141    }
142
143    fn pop_env(&mut self) {
144        let last_env = self.environment_stack.last();
145
146        match last_env {
147            None => {
148                unreachable!("no top level environment")
149            }
150            Some(_) => {
151                self.environment_stack.pop();
152            }
153        }
154    }
155
156    /// Replace the top environment with an empty one and clear parse-time free-param scopes.
157    /// If there is only one environment layer (builtin), does nothing so builtins stay intact.
158    pub fn clear_current_env_and_parse_name_scope(&mut self) {
159        if self.environment_stack.len() > 1 {
160            if let Some(top) = self.environment_stack.last_mut() {
161                *top = Box::new(Environment::new_empty_env());
162            }
163        }
164        self.parsing_free_param_collection.clear();
165    }
166
167    /// 在临时子环境中执行闭包:`push_env` → `f` → `pop_env`;`Ok`/`Err` 都会弹出。
168    /// 与手写 `push`/`pop` 等价;若闭包 panic,栈不会恢复(与手写相同)。
169    pub fn run_in_local_env<T, E, F>(&mut self, f: F) -> Result<T, E>
170    where
171        F: FnOnce(&mut Self) -> Result<T, E>,
172    {
173        self.push_env();
174        let result = f(self);
175        self.pop_env();
176        result
177    }
178
179    /// Restores [`Runtime::parsing_free_param_collection`] after `f` so parse-time bindings (e.g.
180    /// `have x …` without `=`) do not leak across sibling `prove:` blocks or out of nested parses
181    /// that use this wrapper (`forall`, `exist`, `prove`, `prop` bodies, etc.).
182    pub fn run_in_local_parsing_time_name_scope<T, E, F>(&mut self, f: F) -> Result<T, E>
183    where
184        F: FnOnce(&mut Self) -> Result<T, E>,
185    {
186        let saved_free_params = self.parsing_free_param_collection.clone();
187        let result = f(self);
188        self.parsing_free_param_collection = saved_free_params;
189        result
190    }
191
192    /// `begin_scope` → `f` → `end_scope`; runs `end_scope` on both `Ok` and `Err` (not on `begin_scope` failure).
193    pub fn parse_in_local_free_param_scope<T, F>(
194        &mut self,
195        kind: ParamObjType,
196        names: &[String],
197        line_file: LineFile,
198        f: F,
199    ) -> Result<T, RuntimeError>
200    where
201        F: FnOnce(&mut Self) -> Result<T, RuntimeError>,
202    {
203        self.parsing_free_param_collection
204            .begin_scope(kind, names, line_file)?;
205        let result = f(self);
206        self.parsing_free_param_collection.end_scope(kind, names);
207        result
208    }
209
210    /// If `names` is empty, runs `f` with no extra scope; otherwise wraps it in `parse_in_local_free_param_scope`.
211    pub fn with_optional_free_param_scope<T, F>(
212        &mut self,
213        kind: ParamObjType,
214        names: &[String],
215        line_file: LineFile,
216        f: F,
217    ) -> Result<T, RuntimeError>
218    where
219        F: FnOnce(&mut Self) -> Result<T, RuntimeError>,
220    {
221        if names.is_empty() {
222            f(self)
223        } else {
224            self.parse_in_local_free_param_scope(kind, names, line_file, f)
225        }
226    }
227
228    pub fn parse_stmts_with_optional_free_param_scope<F>(
229        &mut self,
230        kind: ParamObjType,
231        names: &[String],
232        line_file: LineFile,
233        parse_body: F,
234    ) -> Result<Vec<Stmt>, RuntimeError>
235    where
236        F: FnOnce(&mut Self) -> Result<Vec<Stmt>, RuntimeError>,
237    {
238        self.with_optional_free_param_scope(kind, names, line_file, parse_body)
239    }
240}
241
242impl Runtime {
243    pub fn is_name_used_for_identifier_and_field_access(&self, name: &str) -> bool {
244        if is_builtin_identifier_name(name) {
245            return true;
246        }
247
248        for env in self.iter_environments_from_top() {
249            if env.defined_identifiers.contains_key(name) {
250                return true;
251            }
252        }
253
254        false
255    }
256
257    pub fn is_name_used_for_prop(&self, name: &str) -> bool {
258        return self.get_prop_definition_by_name(name).is_some();
259    }
260
261    pub fn is_name_used_for_abstract_prop(&self, name: &str) -> bool {
262        if is_builtin_predicate(name) {
263            return true;
264        }
265
266        return self.get_abstract_prop_definition_by_name(name).is_some();
267    }
268
269    pub fn is_name_used_for_family(&self, name: &str) -> bool {
270        return self.get_family_definition_by_name(name).is_some();
271    }
272
273    pub fn is_name_used_for_algo(&self, name: &str) -> bool {
274        return self.get_algo_definition_by_name(name).is_some();
275    }
276}
277
278impl Runtime {
279    pub fn new_file_and_update_runtime_with_file_content(&mut self, path: &str) {
280        let path_rc: Rc<str> = Rc::from(path);
281        self.module_manager.run_file_paths.push(path_rc.clone());
282        self.module_manager.current_file_index = self.module_manager.run_file_paths.len() - 1;
283        self.module_manager.display_entry_rc = Some(path_rc);
284        self.module_manager.entry_path = path.to_string();
285    }
286
287    pub fn change_file_index_to(&mut self, file_index: usize) {
288        self.module_manager.current_file_index = file_index;
289    }
290}
291
292impl Runtime {
293    pub fn store_tuple_obj_and_cart(
294        &mut self,
295        name: &str,
296        tuple: Option<Tuple>,
297        cart: Option<Cart>,
298        line_file: LineFile,
299    ) {
300        let known_tuple_objs = &mut self.top_level_env().known_objs_equal_to_tuple;
301        let old_tuple_and_cart = known_tuple_objs.get(name).cloned();
302
303        let merged_tuple = match (tuple, old_tuple_and_cart.as_ref()) {
304            (Some(new_tuple), _) => Some(new_tuple),
305            (None, Some((old_tuple, _, _))) => old_tuple.clone(),
306            (None, None) => None,
307        };
308        let merged_cart = match (cart, old_tuple_and_cart.as_ref()) {
309            (Some(new_cart), _) => Some(new_cart),
310            (None, Some((_, old_cart, _))) => old_cart.clone(),
311            (None, None) => None,
312        };
313        let merged_line_file = line_file;
314
315        known_tuple_objs.insert(
316            name.to_string(),
317            (merged_tuple, merged_cart, merged_line_file),
318        );
319    }
320
321    pub fn store_known_cart_obj(&mut self, name: &str, cart: Cart, line_file: LineFile) {
322        self.top_level_env()
323            .known_objs_equal_to_cart
324            .insert(name.to_string(), (cart, line_file));
325    }
326
327    pub fn store_known_finite_seq_list_obj(
328        &mut self,
329        name: &str,
330        list: FiniteSeqListObj,
331        member_of_finite_seq_set: Option<FiniteSeqSet>,
332        line_file: LineFile,
333    ) {
334        let map = &mut self.top_level_env().known_objs_equal_to_finite_seq_list;
335        let old = map.get(name).cloned();
336        let merged_member = match (member_of_finite_seq_set, old.as_ref()) {
337            (Some(new_s), _) => Some(new_s),
338            (None, Some((_, Some(old_s), _))) => Some(old_s.clone()),
339            (None, _) => None,
340        };
341        map.insert(name.to_string(), (list, merged_member, line_file));
342    }
343
344    pub fn store_known_matrix_list_obj(
345        &mut self,
346        name: &str,
347        matrix: MatrixListObj,
348        member_of_matrix_set: Option<MatrixSet>,
349        line_file: LineFile,
350    ) {
351        let map = &mut self.top_level_env().known_objs_equal_to_matrix_list;
352        let old = map.get(name).cloned();
353        let merged_member = match (member_of_matrix_set, old.as_ref()) {
354            (Some(new_s), _) => Some(new_s),
355            (None, Some((_, Some(old_s), _))) => Some(old_s.clone()),
356            (None, _) => None,
357        };
358        map.insert(name.to_string(), (matrix, merged_member, line_file));
359    }
360
361    pub fn matrix_set_to_fn_set(&self, ms: &MatrixSet, line_file: LineFile) -> FnSet {
362        let pair = self.generate_random_unused_names(2);
363        let p1 = pair[0].clone();
364        let p2 = pair[1].clone();
365        FnSet::new(
366            vec![
367                ParamGroupWithSet::new(vec![p1.clone()], StandardSet::NPos.into()),
368                ParamGroupWithSet::new(vec![p2.clone()], StandardSet::NPos.into()),
369            ],
370            vec![
371                AtomicFact::from(LessEqualFact::new(
372                    obj_for_bound_param_in_scope(p1, ParamObjType::FnSet),
373                    (*ms.row_len).clone(),
374                    line_file.clone(),
375                ))
376                .into(),
377                AtomicFact::from(LessEqualFact::new(
378                    obj_for_bound_param_in_scope(p2, ParamObjType::FnSet),
379                    (*ms.col_len).clone(),
380                    line_file.clone(),
381                ))
382                .into(),
383            ],
384            (*ms.set).clone(),
385        )
386        .expect("generated matrix fn set uses fresh parameters")
387    }
388
389    pub fn finite_seq_set_to_fn_set(&self, fs: &FiniteSeqSet, line_file: LineFile) -> FnSet {
390        let param = self.generate_random_unused_name();
391        FnSet::new(
392            vec![ParamGroupWithSet::new(
393                vec![param.clone()],
394                StandardSet::NPos.into(),
395            )],
396            vec![AtomicFact::from(LessEqualFact::new(
397                obj_for_bound_param_in_scope(param, ParamObjType::FnSet),
398                (*fs.n).clone(),
399                line_file,
400            ))
401            .into()],
402            (*fs.set).clone(),
403        )
404        .expect("generated finite sequence fn set uses a fresh parameter")
405    }
406
407    pub fn seq_set_to_fn_set(&self, ss: &SeqSet, _line_file: LineFile) -> FnSet {
408        let param = self.generate_random_unused_name();
409        FnSet::new(
410            vec![ParamGroupWithSet::new(
411                vec![param.clone()],
412                StandardSet::NPos.into(),
413            )],
414            vec![],
415            (*ss.set).clone(),
416        )
417        .expect("generated sequence fn set uses a fresh parameter")
418    }
419
420    pub fn finite_seq_set_to_fn_set_from_surface_dom_param(
421        &self,
422        fs: &FiniteSeqSet,
423        line_file: LineFile,
424        surface_dom_param: &str,
425    ) -> Result<FnSet, RuntimeError> {
426        let params = vec![ParamGroupWithSet::new(
427            vec![surface_dom_param.to_string()],
428            StandardSet::NPos.into(),
429        )];
430        let dom_facts: Vec<OrAndChainAtomicFact> = vec![OrAndChainAtomicFact::AtomicFact(
431            LessEqualFact::new(
432                Identifier::new(surface_dom_param.to_string()).into(),
433                (*fs.n).clone(),
434                line_file,
435            )
436            .into(),
437        )];
438        self.new_fn_set(params, dom_facts, (*fs.set).clone())
439    }
440
441    pub fn store_well_defined_obj_cache(&mut self, obj: &Obj) {
442        self.top_level_env()
443            .cache_well_defined_obj
444            .insert(obj.to_string(), ());
445    }
446}
447
448impl Runtime {
449    pub fn new_fn_set(
450        &self,
451        params_and_their_sets: Vec<ParamGroupWithSet>,
452        dom_facts: Vec<OrAndChainAtomicFact>,
453        ret_set: Obj,
454    ) -> Result<FnSet, RuntimeError> {
455        let empty: HashMap<String, Obj> = HashMap::new();
456        let mut dom_stored = Vec::with_capacity(dom_facts.len());
457        for d in &dom_facts {
458            dom_stored.push(self.inst_or_and_chain_atomic_fact(
459                d,
460                &empty,
461                ParamObjType::FnSet,
462                None,
463            )?);
464        }
465        let ret_stored = self.inst_obj(&ret_set, &empty, ParamObjType::FnSet)?;
466        Ok(FnSet::new(params_and_their_sets, dom_stored, ret_stored)?)
467    }
468
469    pub fn new_anonymous_fn(
470        &self,
471        params_and_their_sets: Vec<ParamGroupWithSet>,
472        dom_facts: Vec<OrAndChainAtomicFact>,
473        ret_set: Obj,
474        equal_to: Obj,
475    ) -> Result<AnonymousFn, RuntimeError> {
476        let empty: HashMap<String, Obj> = HashMap::new();
477        let mut dom_stored = Vec::with_capacity(dom_facts.len());
478        for d in &dom_facts {
479            dom_stored.push(self.inst_or_and_chain_atomic_fact(
480                d,
481                &empty,
482                ParamObjType::FnSet,
483                None,
484            )?);
485        }
486        let ret_stored = self.inst_obj(&ret_set, &empty, ParamObjType::FnSet)?;
487        let eq_stored = self.inst_obj(&equal_to, &empty, ParamObjType::FnSet)?;
488        Ok(AnonymousFn::new(
489            params_and_their_sets,
490            dom_stored,
491            ret_stored,
492            eq_stored,
493        )?)
494    }
495
496    pub fn fn_set_from_fn_set_clause(&self, clause: &FnSetClause) -> Result<FnSet, RuntimeError> {
497        self.new_fn_set(
498            clause.params_def_with_set.clone(),
499            clause.dom_facts.clone(),
500            clause.ret_set.clone(),
501        )
502    }
503}
504
505impl Runtime {
506    pub fn params_to_arg_map(
507        &self,
508        param_defs: &ParamDefWithType,
509        args: &[Obj],
510    ) -> Result<HashMap<String, Obj>, RuntimeError> {
511        let param_names = param_defs.collect_param_names();
512        if param_names.len() != args.len() {
513            return Err(
514                InstantiateRuntimeError(RuntimeErrorStruct::new_with_just_msg(format!(
515                    "params_to_arg_map: expected {} argument(s), got {}",
516                    param_names.len(),
517                    args.len()
518                )))
519                .into(),
520            );
521        }
522
523        let mut result: HashMap<String, Obj> = HashMap::new();
524        for (param_name, arg) in param_names.iter().zip(args.iter()) {
525            result.insert(param_name.clone(), arg.clone());
526        }
527        Ok(result)
528    }
529}