roan_engine/
context.rs

1use crate::{
2    module::{loaders::ModuleLoader, Module},
3    vm::VM,
4};
5use anyhow::Result;
6use bon::bon;
7use roan_error::print_diagnostic;
8use std::{cell::RefCell, fmt::Debug, path::PathBuf, rc::Rc};
9use tracing::debug;
10
11/// Struct to interact with the runtime.
12///
13/// # Example
14/// ```rs
15/// let ctx = Context::new();
16/// let src_code = r#"
17/// use { println, eprintln } from "std::io";
18///
19/// fn add(a: float, b: float) -> float {
20///     return a + b;
21/// }
22///
23/// fn main() -> int {
24///     let i = 3.14;
25///     let j = true;
26///
27///     if j {
28///         i = add(i, 2.0);
29///     } else {
30///         eprintln("Goodbye, world!");
31///     }
32///
33///     return 0;
34/// }
35///
36/// main();
37/// "#;
38///
39/// let source = Source::from_string(src_code);
40/// let module = Module::from_source(source, ctx)?;
41///
42/// let result = ctx.eval(module);
43///
44/// assert_eq!(result, Ok(Value::Int(3)));
45/// ```
46#[derive(Clone, Debug)]
47pub struct Context {
48    pub module_loader: Rc<RefCell<dyn ModuleLoader>>,
49    pub cwd: PathBuf,
50}
51
52#[bon]
53impl Context {
54    /// Create a new context.
55    #[builder]
56    pub fn new(
57        #[builder] module_loader: Rc<RefCell<dyn ModuleLoader>>,
58        #[builder(default = std::env::current_dir().unwrap())] cwd: PathBuf,
59    ) -> Self {
60        Self { module_loader, cwd }
61    }
62}
63
64impl Context {
65    /// Evaluate a module by parsing and interpreting it.
66    ///
67    /// # Arguments
68    ///
69    /// * `module` - The module to evaluate.
70    /// * `vm` - The virtual machine instance.
71    ///
72    /// # Returns
73    ///
74    /// The result of the evaluation.
75    pub fn eval(&mut self, module: &mut Module, vm: &mut VM) -> Result<()> {
76        self.parse(module)?;
77
78        self.interpret(module, vm)?;
79
80        Ok(())
81    }
82
83    /// Parse a module to prepare it for interpretation.
84    ///
85    /// # Arguments
86    ///
87    /// * `module` - The module to parse.
88    ///
89    /// # Returns
90    ///
91    /// An empty result if successful, otherwise returns an error.
92    pub fn parse(&mut self, module: &mut Module) -> Result<()> {
93        match module.parse() {
94            Ok(_) => Ok(()),
95            Err(e) => {
96                print_diagnostic(e, Some(module.source().content()));
97                std::process::exit(1);
98            }
99        }
100    }
101
102    /// Interpret a parsed module in the virtual machine.
103    ///
104    /// # Arguments
105    ///
106    /// * `module` - The module to interpret.
107    /// * `vm` - The virtual machine instance.
108    ///
109    /// # Returns
110    ///
111    /// An empty result if successful, otherwise returns an error.
112    pub fn interpret(&mut self, module: &mut Module, vm: &mut VM) -> Result<()> {
113        match module.interpret(self, vm) {
114            Ok(_) => Ok(()),
115            Err(e) => {
116                print_diagnostic(e, Some(module.source().content()));
117                std::process::exit(1);
118            }
119        }
120    }
121
122    /// Insert a module into the context.
123    ///
124    /// # Arguments
125    /// - `name` - The name of the module.
126    /// - `module` - The module to insert.
127    pub fn insert_module(&mut self, name: String, module: Module) {
128        debug!("Inserting module: {}", name);
129        self.module_loader.borrow_mut().insert(name, module);
130    }
131
132    /// Query a module from the context.
133    ///
134    /// # Arguments
135    /// - `name` - The name of the module to query.
136    pub fn query_module(&self, name: &str) -> Option<Module> {
137        self.module_loader.borrow().get(name)
138    }
139
140    /// Load a module from the context.
141    ///
142    /// This function is different from `query_module` in that it will attempt to load the module from the cache
143    /// if it is not found it will try to resolve the path and load the module.
144    ///
145    /// # Arguments
146    /// - `referrer` - The module that is requesting the module.
147    /// - `spec` - The name of the module to load.
148    pub fn load_module(&mut self, referrer: &Module, spec: &str) -> Result<Module> {
149        Ok(self.module_loader.borrow_mut().load(referrer, spec, self)?)
150    }
151
152    pub fn module_keys(&self) -> Vec<String> {
153        self.module_loader.borrow().keys()
154    }
155
156    /// Inserts or updates a module in the context.
157    pub fn upsert_module(&mut self, name: String, module: Module) {
158        debug!("Upserting module: {}", name);
159        self.module_loader.borrow_mut().insert(name, module);
160    }
161}