mimium_lang/
lib.rs

1//! Main module of compiler and runtime for **mimium**, an infrastructural programming language for sound and music.
2
3pub mod ast;
4pub mod interner;
5pub mod interpreter;
6pub mod mir;
7pub mod pattern;
8pub mod types;
9pub mod utils;
10
11pub mod compiler;
12pub mod runtime;
13
14pub mod plugin;
15
16use std::path::PathBuf;
17
18use crate::plugin::{MachineFunction, MacroFunction};
19use compiler::IoChannelInfo;
20pub use log;
21use plugin::{DynSystemPlugin, ExtFunTypeInfo, Plugin, SystemPlugin};
22use runtime::vm::{self, Program, ReturnCode};
23use utils::error::ReportableError;
24
25// #[cfg(not(target_arch = "wasm32"))]
26// use mimalloc::MiMalloc;
27
28// #[cfg(not(target_arch = "wasm32"))]
29// #[global_allocator]
30// static GLOBAL: MiMalloc = MiMalloc;
31
32/// Configuration for the compiler and runtime.
33#[derive(Debug, Clone, Copy, Default)]
34pub struct Config {
35    pub compiler: compiler::Config,
36    // pub runtime: runtime::Config,
37}
38
39/// Container for compiler context, virtual machine and plugins.
40///
41/// [`ExecContext`] is used as a high level API when running mimium from other
42/// Rust programs.  After adding desired plugins, call
43/// [`prepare_machine`](ExecContext::prepare_machine) to compile the source and
44/// create a [`vm::Machine`].
45pub struct ExecContext {
46    compiler: Option<compiler::Context>,
47    vm: Option<runtime::vm::Machine>,
48    plugins: Vec<Box<dyn Plugin>>,
49    sys_plugins: Vec<DynSystemPlugin>,
50    path: Option<PathBuf>,
51    config: Config,
52}
53
54impl ExecContext {
55    //The Argument will be changed to the plugins, when the plugin system is introduced
56    /// Create a new execution context with the given plugins and configuration.
57    pub fn new(
58        plugins: impl Iterator<Item = Box<dyn Plugin>>,
59        path: Option<PathBuf>,
60        config: Config,
61    ) -> Self {
62        let plugins = plugins
63            .chain([plugin::get_builtin_fns_as_plugins()])
64            .collect::<Vec<_>>();
65
66        let sys_plugins = vec![];
67        Self {
68            compiler: None,
69            vm: None,
70            plugins,
71            sys_plugins,
72            path,
73            config,
74        }
75    }
76    pub fn add_plugin<T: Plugin + 'static>(&mut self, plug: T) {
77        self.plugins.push(Box::new(plug))
78    }
79    pub fn get_plugins(&self) -> impl ExactSizeIterator<Item = &Box<dyn Plugin>> {
80        self.plugins.iter()
81    }
82    pub fn get_system_plugins(&self) -> impl ExactSizeIterator<Item = &DynSystemPlugin> {
83        self.sys_plugins.iter()
84    }
85    //todo: make it to builder pattern
86    pub fn add_system_plugin<T: SystemPlugin + 'static>(&mut self, plug: T) {
87        let plugin_dyn = DynSystemPlugin::from(plug);
88
89        self.sys_plugins.push(plugin_dyn)
90    }
91    pub fn get_compiler(&self) -> Option<&compiler::Context> {
92        self.compiler.as_ref()
93    }
94    pub fn take_compiler(&mut self) -> Option<compiler::Context> {
95        self.compiler.take()
96    }
97    pub fn take_vm(&mut self) -> Option<runtime::vm::Machine> {
98        self.vm.take()
99    }
100    pub fn get_vm(&self) -> Option<&runtime::vm::Machine> {
101        self.vm.as_ref()
102    }
103    pub fn get_compiler_mut(&mut self) -> Option<&mut compiler::Context> {
104        self.compiler.as_mut()
105    }
106    pub fn get_vm_mut(&mut self) -> Option<&mut runtime::vm::Machine> {
107        self.vm.as_mut()
108    }
109    fn get_extfun_types(&self) -> Vec<ExtFunTypeInfo> {
110        plugin::get_extfun_types(&self.plugins)
111            .chain(
112                self.sys_plugins
113                    .iter()
114                    .flat_map(|p| p.clsinfos.clone().into_iter().map(ExtFunTypeInfo::from)),
115            )
116            .collect()
117    }
118    fn get_macro_types(&self) -> Vec<Box<dyn MacroFunction>> {
119        plugin::get_macro_functions(&self.plugins)
120            .chain(self.sys_plugins.iter().flat_map(|p| {
121                p.macroinfos
122                    .iter()
123                    .map(|i| Box::new(i.clone()) as Box<dyn MacroFunction>)
124            }))
125            .collect()
126    }
127    pub fn prepare_compiler(&mut self) {
128        let macroinfos = plugin::get_macro_functions(&self.plugins).chain(
129            self.sys_plugins.iter().flat_map(|p| {
130                p.macroinfos
131                    .iter()
132                    .map(|i| Box::new(i.clone()) as Box<dyn MacroFunction>)
133            }),
134        );
135        self.compiler = Some(compiler::Context::new(
136            self.get_extfun_types(),
137            macroinfos,
138            self.path.clone(),
139            self.config.compiler,
140        ));
141    }
142    /// Compile `src` and prepare an executable VM.
143    pub fn prepare_machine(&mut self, src: &str) -> Result<(), Vec<Box<dyn ReportableError>>> {
144        if self.compiler.is_none() {
145            self.prepare_compiler();
146        }
147
148        let prog = self.compiler.as_ref().unwrap().emit_bytecode(src)?;
149        self.prepare_machine_with_bytecode(prog);
150        Ok(())
151    }
152
153    /// Build a VM from the given bytecode [`Program`].
154    pub fn prepare_machine_with_bytecode(&mut self, prog: Program) {
155        let cls =
156            plugin::get_ext_closures(&self.plugins).chain(self.sys_plugins.iter().flat_map(|p| {
157                p.clsinfos
158                    .clone()
159                    .into_iter()
160                    .map(|c| Box::new(c) as Box<dyn MachineFunction>)
161            }));
162        let vm = vm::Machine::new(prog, [].into_iter(), cls);
163        self.vm = Some(vm);
164    }
165
166    pub fn get_iochannel_count(&self) -> Option<IoChannelInfo> {
167        self.vm.as_ref().and_then(|vm| vm.prog.iochannels)
168    }
169    /// Execute the `main` function in the loaded program.
170    pub fn run_main(&mut self) -> ReturnCode {
171        if let Some(vm) = self.vm.as_mut() {
172            self.sys_plugins.iter().for_each(|plug: &DynSystemPlugin| {
173                //todo: encapsulate unsafety within SystemPlugin functionality
174                let p = unsafe { plug.inner.get().as_mut().unwrap_unchecked() };
175                let _ = p.on_init(vm);
176            });
177            let res = vm.execute_main();
178            self.sys_plugins.iter().for_each(|plug: &DynSystemPlugin| {
179                //todo: encapsulate unsafety within SystemPlugin functionality
180                let p = unsafe { plug.inner.get().as_mut().unwrap_unchecked() };
181                let _ = p.after_main(vm);
182            });
183            res
184        } else {
185            0
186        }
187    }
188    pub fn try_get_main_loop(&mut self) -> Option<Box<dyn FnOnce()>> {
189        let mut mainloops = self.sys_plugins.iter_mut().filter_map(|p| {
190            let p = unsafe { p.inner.get().as_mut().unwrap_unchecked() };
191            p.try_get_main_loop()
192        });
193        let res = mainloops.next();
194        if mainloops.next().is_some() {
195            log::warn!("more than 2 main loops in system plugins found")
196        }
197        res
198    }
199}
200//todo: remove
201// pub mod ast_interpreter;
202// pub mod repl;