Skip to main content

unlab_gpu/
env.rs

1//
2// Copyright (c) 2025-2026 Ɓukasz Szpakowski
3//
4// This Source Code Form is subject to the terms of the Mozilla Public
5// License, v. 2.0. If a copy of the MPL was not distributed with this
6// file, You can obtain one at https://mozilla.org/MPL/2.0/.
7//
8//! An environment module.
9use std::borrow::Cow;
10use std::collections::BTreeMap;
11use std::collections::HashSet;
12use std::ffi::OsStr;
13use std::ffi::OsString;
14use std::io::Cursor;
15use std::path::Path;
16use std::path::PathBuf;
17use std::sync::Arc;
18use std::sync::RwLock;
19use std::time::Instant;
20#[cfg(feature = "plot")]
21use crate::winit;
22use crate::error::*;
23use crate::intr::*;
24use crate::mod_node::*;
25#[cfg(feature = "plot")]
26use crate::plot::*;
27use crate::tree::*;
28use crate::utils::*;
29use crate::value::*;
30
31/// An input enumeration.
32#[derive(Copy, Clone, Debug)]
33pub enum Input
34{
35    /// A standard input.
36    Std,
37    /// A null input.
38    Null,
39}
40
41/// An output enumeration.
42#[derive(Clone, Debug)]
43pub enum Output
44{
45    /// A standard output.
46    Std,
47    /// A null output.
48    Null,
49    /// A cursor.
50    Cursor(Arc<RwLock<Cursor<Vec<u8>>>>),
51}
52
53/// A type of event loop proxy.
54#[cfg(feature = "plot")]
55pub type EventLoopProxy = winit::event_loop::EventLoopProxy<PlotterAppEvent>;
56
57/// A type of event loop proxy.
58#[cfg(not(feature = "plot"))]
59#[derive(Clone, Debug)]
60pub struct EventLoopProxy(());
61
62/// A structure of shared environment.
63///
64/// The shared environment is part of an environment that has global properities which can be
65/// shared between different environments. These global properties are library paths and
66/// documentation paths, arguments, used libraries, test suites, an event loop proxy, and other
67/// properties. The event loop proxy is used to communication to with the main thread from the
68/// windows.
69#[derive(Clone)]
70pub struct SharedEnv
71{
72    lib_path: OsString,
73    doc_path: OsString,
74    args: Vec<String>,
75    used_libs: HashSet<String>,
76    test_suites: HashSet<Vec<String>>,
77    intr_checker: Arc<dyn IntrCheck + Send + Sync>,
78    event_loop_proxy: Option<EventLoopProxy>,
79    instant: Instant,
80}
81
82impl SharedEnv
83{
84    /// Creates a shared environment with the interrruption checker and the event loop proxy.
85    ///
86    /// Also, this method takes the paths of the libraries and the documentations and the
87    /// arguments.
88    pub fn new_with_intr_checker_and_event_loop_proxy(lib_path: OsString, doc_path: OsString, args: Vec<String>, intr_checker: Arc<dyn IntrCheck + Send + Sync>, event_loop_proxy: Option<EventLoopProxy>) -> Self
89    {
90        SharedEnv {
91            lib_path,
92            doc_path,
93            args,
94            used_libs: HashSet::new(),
95            test_suites: HashSet::new(),
96            intr_checker,
97            event_loop_proxy,
98            instant: Instant::now(),
99        }
100    }
101
102    /// Creates a shared environment with the interrruption checker.
103    ///
104    /// See [`new_with_intr_checker_and_event_loop_proxy`](Self::new_with_intr_checker_and_event_loop_proxy).
105    pub fn new_with_intr_checker(lib_path: OsString, doc_path: OsString, args: Vec<String>, intr_checker: Arc<dyn IntrCheck + Send + Sync>) -> Self
106    { Self::new_with_intr_checker_and_event_loop_proxy(lib_path, doc_path, args, intr_checker, None) }
107
108    /// Creates a shared environment.
109    ///
110    /// See [`new_with_intr_checker_and_event_loop_proxy`](Self::new_with_intr_checker_and_event_loop_proxy).
111    pub fn new(lib_path: OsString, doc_path: OsString, args: Vec<String>) -> Self
112    { Self::new_with_intr_checker(lib_path, doc_path, args, Arc::new(EmptyIntrChecker::new())) }
113
114    /// Returns the library paths.
115    pub fn lib_path(&self) -> &OsStr
116    { self.lib_path.as_os_str() }
117
118    /// Returns the documentation paths.
119    pub fn doc_path(&self) -> &OsStr
120    { self.doc_path.as_os_str() }
121
122    /// Returns the arguments.
123    pub fn args(&self) -> &[String]
124    { self.args.as_slice() }
125
126    /// Returns the used libraries.
127    pub fn used_libs(&self) -> &HashSet<String>
128    { &self.used_libs }
129
130    /// Returns `true` if the shared environment has the used libraries, otherwise `false`.
131    pub fn has_used_lib(&self, lib: &String) -> bool
132    { self.used_libs.contains(lib) }
133
134    /// Adds used library to the shared environment.
135    pub fn add_used_lib(&mut self, lib: String)
136    { self.used_libs.insert(lib); }
137
138    /// Removes used library from the shared environment.
139    pub fn remove_used_lib(&mut self, lib: &String)
140    { self.used_libs.remove(lib); }
141
142    /// Returns the test suites.
143    pub fn test_suites(&self) -> &HashSet<Vec<String>>
144    { &self.test_suites }
145
146    /// Returns `true` if the shared environment has the test suite, otherwise `false`.
147    pub fn has_test_suite(&self, idents: &Vec<String>) -> bool
148    { self.test_suites.contains(idents) }
149
150    /// Adds test suite to the shared environment.
151    pub fn add_test_suite(&mut self, idents: Vec<String>)
152    { self.test_suites.insert(idents); }
153
154    /// Removes test suite from the shared environment.
155    pub fn remove_test_suite(&mut self, idents: &Vec<String>)
156    { self.test_suites.remove(idents); }    
157
158    /// Returns the interruption checker.
159    pub fn intr_checker(&self) -> &Arc<dyn IntrCheck + Send + Sync>
160    { &self.intr_checker }
161    
162    /// Returns the event loop proxy if the shared environment has the event loop proxy, otherwise
163    /// `None`.
164    pub fn event_loop_proxy(&self) -> Option<&EventLoopProxy>
165    {
166        match &self.event_loop_proxy {
167            Some(event_loop_proxy) => Some(event_loop_proxy),
168            None => None,
169        }
170    }
171    
172    /// Returns the measurement of system clock.
173    pub fn instant(&self) -> &Instant
174    { &self.instant }
175}
176
177/// An environment structure.
178///
179/// The environment is used by an interpreter and built-in functions and contains modules,
180/// variables, and a stack which are available by methods of the environment. Each stack element
181/// has a function module and local variables. Also, the environment has a script directory, a
182/// domain, the standard I/O enumerations, and a shared environment. The standard I/O enumerations
183/// determine which streams are used to the standard I/O in built-in functions.
184#[derive(Clone)]
185pub struct Env
186{
187    root_mod: Arc<RwLock<ModNode<Value, ()>>>,
188    current_mod: Arc<RwLock<ModNode<Value, ()>>>,
189    mod_idents: Vec<String>,
190    stack: Vec<(Arc<RwLock<ModNode<Value, ()>>>, BTreeMap<String, Value>)>,
191    script_dir: PathBuf,
192    domain: Option<String>,
193    stdin: Input,
194    stdout: Output,
195    stderr: Output, 
196    shared_env: Arc<RwLock<SharedEnv>>,
197}
198
199impl Env
200{
201    /// Creates an environment with the path to script directory, the domain, and the shared
202    /// environment.
203    ///
204    /// Also, this method takes the root module that has the variables and other modules. 
205    pub fn new_with_script_dir_and_domain_and_shared_env(root_mod: Arc<RwLock<ModNode<Value, ()>>>, script_dir: PathBuf, domain: Option<String>, shared_env: Arc<RwLock<SharedEnv>>) -> Self
206    {
207        Env {
208            root_mod: root_mod.clone(),
209            current_mod: root_mod,
210            mod_idents: Vec::new(),
211            stack: Vec::new(),
212            script_dir,
213            domain,
214            stdin: Input::Std,
215            stdout: Output::Std,
216            stderr: Output::Std,
217            shared_env,
218        }
219    }
220
221    /// Creates an environment.
222    ///
223    /// See [`new_with_script_dir_and_domain_and_shared_env`](Self::new_with_script_dir_and_domain_and_shared_env).
224    pub fn new(root_mod: Arc<RwLock<ModNode<Value, ()>>>) -> Self
225    { Self::new_with_script_dir_and_domain_and_shared_env(root_mod, PathBuf::from("."), None, Arc::new(RwLock::new(SharedEnv::new(OsString::from("."), OsString::from("."), Vec::new())))) }
226
227    /// Clones the environment without the stack.
228    pub fn clone_without_stack(&self) -> Self
229    {
230        Env {
231            root_mod: self.root_mod.clone(),
232            current_mod: self.current_mod.clone(),
233            mod_idents: self.mod_idents.clone(),
234            stack: Vec::new(),
235            script_dir: self.script_dir.clone(),
236            domain: self.domain.clone(),
237            stdin: self.stdin,
238            stdout: self.stdout.clone(),
239            stderr: self.stderr.clone(),
240            shared_env: self.shared_env.clone(),
241        }
242    }
243
244    /// Returns the root module.
245    pub fn root_mod(&self) -> &Arc<RwLock<ModNode<Value, ()>>>
246    { &self.root_mod }
247
248    /// Returns the current module.
249    pub fn current_mod(&self) -> &Arc<RwLock<ModNode<Value, ()>>>
250    { &self.current_mod }
251
252    /// Returns the identifiers of current module. 
253    pub fn mod_idents(&self) -> &[String]
254    { self.mod_idents.as_slice() }
255    
256    /// Returns the stack.
257    pub fn stack(&self) -> &[(Arc<RwLock<ModNode<Value, ()>>>, BTreeMap<String, Value>)]
258    { self.stack.as_slice() }
259
260    /// Returns the path to script directory.
261    pub fn script_dir(&self) -> &Path
262    { self.script_dir.as_path() }
263
264    /// Returns the domain.
265    pub fn domain(&self) -> Option<&str>
266    { 
267        match &self.domain {
268            Some(domain) => Some(domain.as_str()),
269            None => None,
270        }
271    }
272    
273    /// Returns the standard input enumeration.
274    pub fn stdin(&self) -> &Input
275    { &self.stdin }
276    
277    /// Sets the standart input enumeration.
278    pub fn set_stdin(&mut self, input: Input)
279    { self.stdin = input; }
280
281    /// Returns the standard output enumeration.
282    pub fn stdout(&self) -> &Output
283    { &self.stdout }
284    
285    /// Sets the standard output enumeration.
286    pub fn set_stdout(&mut self, output: Output)
287    { self.stdout = output; }
288
289    /// Returns the standard error enumeration.
290    pub fn stderr(&self) -> &Output
291    { &self.stderr }
292    
293    /// Sets the standard error enumeration.
294    pub fn set_stderr(&mut self, output: Output)
295    { self.stderr = output; }
296    
297    /// Retunrs the shared environment.
298    pub fn shared_env(&self) -> &Arc<RwLock<SharedEnv>>
299    { &self.shared_env }
300    
301    /// Adds an empty module to the current module and then the empty module is set as the current
302    /// module.
303    ///
304    /// This method adds the empty, sets the empty module as current, and returns `true` if a
305    /// module with the identifier doesn't exist in the current module; otherwise this method
306    /// returns `false`.
307    pub fn add_and_push_mod(&mut self, ident: String) -> Result<bool>
308    {
309        {
310            let current_mod_g = rw_lock_read(&self.current_mod)?;
311            if current_mod_g.has_mod(&ident) {
312                return Ok(false);
313            }
314        }
315        let new_mod: Arc<RwLock<ModNode<Value, ()>>> = Arc::new(RwLock::new(ModNode::new(())));
316        ModNode::add_mod(&self.current_mod, ident.clone(), new_mod.clone())?;
317        self.current_mod = new_mod;
318        self.mod_idents.push(ident);
319        Ok(true)
320    }
321    
322    /// Sets the parent module of the current module is set as the current module.
323    ///
324    /// This method sets the parent module as current and returns `true` if the current module has
325    /// the parent module, otherwise this method returns `false`.
326    pub fn pop_mod(&mut self) -> Result<bool>
327    {
328        let parent = {
329            let current_mod_g = rw_lock_read(&self.current_mod)?;
330            current_mod_g.parent()
331        };
332        match parent {
333            Some(parent) => {
334                self.current_mod = parent;
335                self.mod_idents.pop();
336                Ok(true)
337            },
338            None => Ok(false),
339        }
340    }
341
342    /// Adds the function.
343    ///
344    /// This method adds the function and returns `true` if a function or a variable with the
345    /// identifier doesn't exist in the current module, otherwise this method returns`false`.
346    pub fn add_fun(&self, ident: String, fun: Arc<Fun>) -> Result<bool>
347    {
348        let mut current_mod_g = rw_lock_write(&self.current_mod)?;
349        if current_mod_g.has_var(&ident) {
350            return Ok(false);
351        }
352        current_mod_g.add_var(ident.clone(), Value::Object(Arc::new(Object::Fun(self.mod_idents.clone(), ident, fun))));
353        Ok(true)
354    }
355
356    /// Pushes the function module and a local variables to the stack for applies the function.
357    ///
358    /// This method pushes the function module and the local variables and returns `true` if all
359    /// identifiers of arguments are different, otherwise this method returns `false`. 
360    pub fn push_fun_mod_and_local_vars(&mut self, fun_mod_idents: &[String], args: &[Arg], arg_values: &[Value]) -> Result<bool>
361    {
362        let fun_mod = match ModNode::mod_from(&self.root_mod, fun_mod_idents, false)? {
363            Some(tmp_fun_mod) => tmp_fun_mod,
364            None => return Err(Error::NoFunMod),
365        };
366        if args.len() != arg_values.len() {
367            return Ok(false);
368        }
369        let mut local_vars: BTreeMap<String, Value> = BTreeMap::new();
370        for (arg, value) in args.iter().zip(arg_values.iter()) {
371            match arg {
372                Arg(ident, _) => {
373                    local_vars.insert(ident.clone(), value.clone());
374                },
375            }
376        }
377        self.stack.push((fun_mod, local_vars));
378        Ok(true)
379    }
380
381    /// Removes the last functoin module and the last local varables from stack.
382    pub fn pop_fun_mod_and_local_vars(&mut self)
383    { self.stack.pop(); }
384    
385    /// Resets the environment.
386    pub fn reset(&mut self) -> Result<()>
387    {
388        match self.mod_idents.first() { 
389            Some(first_ident) => {
390                let mut root_mod_g = rw_lock_write(&self.root_mod)?;
391                root_mod_g.remove_mod(first_ident)?;
392            },
393            None => (),
394        }
395        self.current_mod = self.root_mod.clone();
396        self.mod_idents.clear();
397        self.stack.clear();
398        Ok(())
399    }
400    
401    fn mod_tuple_for_name<'a>(&self, name: &'a Name, is_var: &mut bool, is_set: bool) -> Result<(Option<Arc<RwLock<ModNode<Value, ()>>>>, Cow<'a, String>, Option<Value>)>
402    {
403        *is_var = false;
404        match name {
405            Name::Abs(idents, ident) => {
406                match ModNode::mod_from(&self.root_mod, idents.as_slice(), false)? {
407                    Some(tmp_mod) => Ok((Some(tmp_mod), Cow::Borrowed(ident), None)),
408                    None => Ok((None, Cow::Borrowed(ident), None)),
409                }
410            },
411            Name::Rel(idents, ident) => {
412                let mod1 = match self.stack.last() {
413                    Some((fun_mod, _)) => fun_mod.clone(),
414                    None => self.current_mod.clone(),
415                };
416                if !idents.is_empty() {
417                    match ModNode::mod_from(&mod1, idents.as_slice(), true)? {
418                        Some(tmp_mod) => Ok((Some(tmp_mod), Cow::Borrowed(ident), None)),
419                        None => {
420                            match ModNode::mod_from(&self.root_mod, idents.as_slice(), false)? {
421                                Some(tmp_mod) => Ok((Some(tmp_mod), Cow::Borrowed(ident), None)),
422                                None => Ok((None, Cow::Borrowed(ident), None)),
423                            }
424                        }
425                    }
426                } else {
427                    let is_defined_var = {
428                        let mod_g = rw_lock_read(&mod1)?;
429                        mod_g.has_var(ident)
430                    };
431                    if is_defined_var {
432                        Ok((Some(mod1), Cow::Borrowed(ident), None))
433                    } else {
434                        let mod_g = rw_lock_read(&mod1)?;
435                        match mod_g.used_var(ident) {
436                            Some(used_var) => Ok((used_var.mod1().to_arc(), Cow::Owned(used_var.ident().clone()), None)),
437                            None => Ok((Some(mod1.clone()), Cow::Borrowed(ident), None)),
438                        }
439                    }
440                }
441            },
442            Name::Var(ident) => {
443                *is_var = true;
444                let mod1 = match self.stack.last() {
445                    Some((fun_mod, _)) => fun_mod.clone(),
446                    None => self.current_mod.clone(),
447                };
448                let local_var_value = if !is_set {
449                    match self.stack.last() {
450                        Some((_, local_vars)) => local_vars.get(ident).map(|v| v.clone()),
451                        None => None, 
452                    }
453                } else {
454                    None
455                };
456                if local_var_value.is_some() || (is_set && !self.stack.is_empty()) {
457                    Ok((Some(mod1), Cow::Borrowed(ident), local_var_value))
458                } else {
459                    let is_defined_var = {
460                        let mod_g = rw_lock_read(&mod1)?;
461                        mod_g.has_var(ident)
462                    };
463                    if is_defined_var {
464                        Ok((Some(mod1), Cow::Borrowed(ident), None))
465                    } else {
466                        let mod_g = rw_lock_read(&mod1)?;
467                        match mod_g.used_var(ident) {
468                            Some(used_var) => Ok((used_var.mod1().to_arc(), Cow::Owned(used_var.ident().clone()), None)),
469                            None => Ok((Some(mod1.clone()), Cow::Borrowed(ident), None)),
470                        }
471                    }
472                }
473            },
474        }
475    }
476
477    /// Returns the variable value for the variable name if the variable exists, otherwise `None`.
478    pub fn var(&self, name: &Name) -> Result<Option<Value>>
479    {
480        let mut is_var = false;
481        let (mod1, ident, value) = self.mod_tuple_for_name(name, &mut is_var, false)?;
482        match value {
483            Some(value) => return Ok(Some(value)),
484            None => (),
485        }
486        match mod1 {
487            Some(mod1) => {
488                let mut value: Option<Value>;
489                {
490                    let mod1_g = rw_lock_read(&mod1)?;
491                    value = mod1_g.var(&*ident).map(|v| v.clone());
492                }
493                if is_var && value.is_none() {
494                    let root_mod_g = rw_lock_read(&self.root_mod)?;
495                    value = root_mod_g.var(&*ident).map(|v| v.clone());
496                }
497                Ok(value)
498            },
499            None => Ok(None),
500        }
501    }
502
503    /// Sets the variable value for the variable name and returns `true` if the variable can be
504    /// set, otherwise this method returns `false`.
505    pub fn set_var(&mut self, name: &Name, value: Value) -> Result<bool>
506    {
507        let mut is_var = false;
508        let (mod1, ident, _) = self.mod_tuple_for_name(name, &mut is_var, true)?;
509        if is_var {
510            match self.stack.last_mut() {
511                Some((_, local_vars)) => {
512                    local_vars.insert(ident.into_owned(), value);
513                    return Ok(true)
514                },
515                None => (),
516            }
517        }
518        match mod1 {
519            Some(mod1) => {
520                let mut mod1_g = rw_lock_write(&mod1)?;
521                mod1_g.add_var(ident.into_owned(), value);
522                Ok(true)
523            },
524            None => Ok(false),
525        }
526    }
527    
528    /// Removes the local variable with the identifier and returns `true` if the stack isn't
529    /// empty, otherwise this method returns `false`.
530    pub fn remove_local_var(&mut self, ident: &String) -> bool
531    {
532        match self.stack.last_mut() {
533            Some((_, local_vars)) => {
534                local_vars.remove(ident);
535                true
536            },
537            None => false,
538        }
539    }
540}
541
542#[cfg(test)]
543mod tests;