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(&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_algo(&self, name: &str) -> bool {
270        return self.get_algo_definition_by_name(name).is_some();
271    }
272}
273
274impl Runtime {
275    pub fn new_file_and_update_runtime_with_file_content(&mut self, path: &str) {
276        let path_rc: Rc<str> = Rc::from(path);
277        self.module_manager.run_file_paths.push(path_rc.clone());
278        self.module_manager.current_file_index = self.module_manager.run_file_paths.len() - 1;
279        self.module_manager.display_entry_rc = Some(path_rc);
280        self.module_manager.entry_path = path.to_string();
281    }
282
283    pub fn change_file_index_to(&mut self, file_index: usize) {
284        self.module_manager.current_file_index = file_index;
285    }
286}
287
288impl Runtime {
289    pub fn store_tuple_obj_and_cart(
290        &mut self,
291        name: &str,
292        tuple: Option<Tuple>,
293        cart: Option<Cart>,
294        line_file: LineFile,
295    ) {
296        let known_tuple_objs = &mut self.top_level_env().known_objs_equal_to_tuple;
297        let old_tuple_and_cart = known_tuple_objs.get(name).cloned();
298
299        let merged_tuple = match (tuple, old_tuple_and_cart.as_ref()) {
300            (Some(new_tuple), _) => Some(new_tuple),
301            (None, Some((old_tuple, _, _))) => old_tuple.clone(),
302            (None, None) => None,
303        };
304        let merged_cart = match (cart, old_tuple_and_cart.as_ref()) {
305            (Some(new_cart), _) => Some(new_cart),
306            (None, Some((_, old_cart, _))) => old_cart.clone(),
307            (None, None) => None,
308        };
309        let merged_line_file = line_file;
310
311        known_tuple_objs.insert(
312            name.to_string(),
313            (merged_tuple, merged_cart, merged_line_file),
314        );
315    }
316
317    pub fn store_known_cart_obj(&mut self, name: &str, cart: Cart, line_file: LineFile) {
318        self.top_level_env()
319            .known_objs_equal_to_cart
320            .insert(name.to_string(), (cart, line_file));
321    }
322
323    pub fn store_known_set_builder_obj(
324        &mut self,
325        name: &str,
326        set_builder: SetBuilder,
327        line_file: LineFile,
328    ) {
329        self.top_level_env()
330            .known_objs_equal_to_set_builder
331            .insert(name.to_string(), (set_builder, line_file));
332    }
333
334    pub fn store_known_finite_seq_list_obj(
335        &mut self,
336        name: &str,
337        list: FiniteSeqListObj,
338        member_of_finite_seq_set: Option<FiniteSeqSet>,
339        line_file: LineFile,
340    ) {
341        let map = &mut self.top_level_env().known_objs_equal_to_finite_seq_list;
342        let old = map.get(name).cloned();
343        let merged_member = match (member_of_finite_seq_set, old.as_ref()) {
344            (Some(new_s), _) => Some(new_s),
345            (None, Some((_, Some(old_s), _))) => Some(old_s.clone()),
346            (None, _) => None,
347        };
348        map.insert(name.to_string(), (list, merged_member, line_file));
349    }
350
351    pub fn store_known_matrix_list_obj(
352        &mut self,
353        name: &str,
354        matrix: MatrixListObj,
355        member_of_matrix_set: Option<MatrixSet>,
356        line_file: LineFile,
357    ) {
358        let map = &mut self.top_level_env().known_objs_equal_to_matrix_list;
359        let old = map.get(name).cloned();
360        let merged_member = match (member_of_matrix_set, old.as_ref()) {
361            (Some(new_s), _) => Some(new_s),
362            (None, Some((_, Some(old_s), _))) => Some(old_s.clone()),
363            (None, _) => None,
364        };
365        map.insert(name.to_string(), (matrix, merged_member, line_file));
366    }
367
368    pub fn matrix_set_to_fn_set(&self, ms: &MatrixSet, line_file: LineFile) -> FnSet {
369        let pair = self.generate_random_unused_names(2);
370        let p1 = pair[0].clone();
371        let p2 = pair[1].clone();
372        FnSet::new(
373            vec![
374                ParamGroupWithSet::new(vec![p1.clone()], StandardSet::NPos.into()),
375                ParamGroupWithSet::new(vec![p2.clone()], StandardSet::NPos.into()),
376            ],
377            vec![
378                AtomicFact::from(LessEqualFact::new(
379                    obj_for_bound_param_in_scope(p1, ParamObjType::FnSet),
380                    (*ms.row_len).clone(),
381                    line_file.clone(),
382                ))
383                .into(),
384                AtomicFact::from(LessEqualFact::new(
385                    obj_for_bound_param_in_scope(p2, ParamObjType::FnSet),
386                    (*ms.col_len).clone(),
387                    line_file.clone(),
388                ))
389                .into(),
390            ],
391            (*ms.set).clone(),
392        )
393        .expect("generated matrix fn set uses fresh parameters")
394    }
395
396    pub fn finite_seq_set_to_fn_set(&self, fs: &FiniteSeqSet, line_file: LineFile) -> FnSet {
397        let param = self.generate_random_unused_name();
398        FnSet::new(
399            vec![ParamGroupWithSet::new(
400                vec![param.clone()],
401                StandardSet::NPos.into(),
402            )],
403            vec![AtomicFact::from(LessEqualFact::new(
404                obj_for_bound_param_in_scope(param, ParamObjType::FnSet),
405                (*fs.n).clone(),
406                line_file,
407            ))
408            .into()],
409            (*fs.set).clone(),
410        )
411        .expect("generated finite sequence fn set uses a fresh parameter")
412    }
413
414    pub fn seq_set_to_fn_set(&self, ss: &SeqSet, _line_file: LineFile) -> FnSet {
415        let param = self.generate_random_unused_name();
416        FnSet::new(
417            vec![ParamGroupWithSet::new(
418                vec![param.clone()],
419                StandardSet::NPos.into(),
420            )],
421            vec![],
422            (*ss.set).clone(),
423        )
424        .expect("generated sequence fn set uses a fresh parameter")
425    }
426
427    pub fn finite_seq_set_to_fn_set_from_surface_dom_param(
428        &self,
429        fs: &FiniteSeqSet,
430        line_file: LineFile,
431        surface_dom_param: &str,
432    ) -> Result<FnSet, RuntimeError> {
433        let params = vec![ParamGroupWithSet::new(
434            vec![surface_dom_param.to_string()],
435            StandardSet::NPos.into(),
436        )];
437        let dom_facts: Vec<OrAndChainAtomicFact> = vec![OrAndChainAtomicFact::AtomicFact(
438            LessEqualFact::new(
439                Identifier::new(surface_dom_param.to_string()).into(),
440                (*fs.n).clone(),
441                line_file,
442            )
443            .into(),
444        )];
445        self.new_fn_set(params, dom_facts, (*fs.set).clone())
446    }
447
448    pub fn store_well_defined_obj_cache(&mut self, obj: &Obj) {
449        self.top_level_env()
450            .cache_well_defined_obj
451            .insert(obj.to_string(), ());
452    }
453}
454
455impl Runtime {
456    pub fn new_fn_set(
457        &self,
458        params_and_their_sets: impl Into<ParamDefWithSet>,
459        dom_facts: Vec<OrAndChainAtomicFact>,
460        ret_set: Obj,
461    ) -> Result<FnSet, RuntimeError> {
462        let empty: HashMap<String, Obj> = HashMap::new();
463        let mut dom_stored = Vec::with_capacity(dom_facts.len());
464        for d in &dom_facts {
465            dom_stored.push(self.inst_or_and_chain_atomic_fact(
466                d,
467                &empty,
468                ParamObjType::FnSet,
469                None,
470            )?);
471        }
472        let ret_stored = self.inst_obj(&ret_set, &empty, ParamObjType::FnSet)?;
473        Ok(FnSet::new(params_and_their_sets, dom_stored, ret_stored)?)
474    }
475
476    pub fn new_anonymous_fn(
477        &self,
478        params_and_their_sets: impl Into<ParamDefWithSet>,
479        dom_facts: Vec<OrAndChainAtomicFact>,
480        ret_set: Obj,
481        equal_to: Obj,
482    ) -> Result<AnonymousFn, RuntimeError> {
483        let empty: HashMap<String, Obj> = HashMap::new();
484        let mut dom_stored = Vec::with_capacity(dom_facts.len());
485        for d in &dom_facts {
486            dom_stored.push(self.inst_or_and_chain_atomic_fact(
487                d,
488                &empty,
489                ParamObjType::FnSet,
490                None,
491            )?);
492        }
493        let ret_stored = self.inst_obj(&ret_set, &empty, ParamObjType::FnSet)?;
494        let eq_stored = self.inst_obj(&equal_to, &empty, ParamObjType::FnSet)?;
495        Ok(AnonymousFn::new(
496            params_and_their_sets,
497            dom_stored,
498            ret_stored,
499            eq_stored,
500        )?)
501    }
502
503    pub fn fn_set_from_fn_set_clause(&self, clause: &FnSetClause) -> Result<FnSet, RuntimeError> {
504        self.new_fn_set(
505            clause.params_def_with_set.clone(),
506            clause.dom_facts.clone(),
507            clause.ret_set.clone(),
508        )
509    }
510}
511
512impl Runtime {
513    pub fn params_to_arg_map(
514        &self,
515        param_defs: &ParamDefWithType,
516        args: &[Obj],
517    ) -> Result<HashMap<String, Obj>, RuntimeError> {
518        let param_names = param_defs.collect_param_names();
519        if param_names.len() != args.len() {
520            return Err(
521                InstantiateRuntimeError(RuntimeErrorStruct::new_with_just_msg(format!(
522                    "params_to_arg_map: expected {} argument(s), got {}",
523                    param_names.len(),
524                    args.len()
525                )))
526                .into(),
527            );
528        }
529
530        let mut result: HashMap<String, Obj> = HashMap::new();
531        for (param_name, arg) in param_names.iter().zip(args.iter()) {
532            result.insert(param_name.clone(), arg.clone());
533        }
534        Ok(result)
535    }
536}