use hashbrown::HashMap; use crate::error::{ErrorPosition, ProjectError}; use crate::interpreter::HandlerTable; use crate::parse::{LexerPlugin, LineParser}; use crate::pipeline::file_loader::{IOResult, Loaded}; use crate::sourcefile::FileEntry; use crate::utils::boxed_iter::box_empty; use crate::utils::BoxedIter; use crate::{ConstTree, Interner, Tok, VName}; /// A description of every point where an external library can hook into Orchid. /// Intuitively, this can be thought of as a plugin pub struct System<'a> { /// An identifier for the system used eg. in error reporting. pub name: Vec, /// External functions and other constant values defined in AST form pub constants: HashMap, ConstTree>, /// Orchid libraries defined by this system pub code: HashMap, /// Prelude lines to be added to **subsequent** systems and usercode to /// expose the functionality of this system. The prelude is not added during /// the loading of this system pub prelude: Vec, /// Handlers for actions defined in this system pub handlers: HandlerTable<'a>, /// Custom lexer for the source code representation atomic data. /// These take priority over builtin lexers so the syntax they /// match should be unambiguous pub lexer_plugins: Vec>, /// Parser that processes custom line types into their representation in the /// module tree pub line_parsers: Vec>, } impl<'a> System<'a> { /// Intern the name of the system so that it can be used as an Orchid /// namespace #[must_use] pub fn vname(&self, i: &Interner) -> VName { self.name.iter().map(|s| i.i(s)).collect::>() } /// Load a file from the system pub fn load_file( &self, path: &[Tok], referrer: &[Tok], ) -> IOResult { (self.code.get(path)).cloned().ok_or_else(|| { let err = MissingSystemCode { path: path.to_vec(), system: self.name.clone(), referrer: referrer.to_vec(), }; err.rc() }) } } /// An error raised when a system fails to load a path. This usually means that /// another system the current one depends on did not get loaded #[derive(Debug)] pub struct MissingSystemCode { path: VName, system: Vec, referrer: VName, } impl ProjectError for MissingSystemCode { fn description(&self) -> &str { "A system tried to import a path that doesn't exist" } fn message(&self) -> String { format!( "Path {} imported by {} is not defined by {} or any system before it", Interner::extern_all(&self.path).join("::"), Interner::extern_all(&self.referrer).join("::"), self.system.join("::") ) } fn positions(&self) -> BoxedIter { box_empty() } } /// Trait for objects that can be converted into a [System] in the presence /// of an [Interner]. pub trait IntoSystem<'a> { /// Convert this object into a system using an interner fn into_system(self, i: &Interner) -> System<'a>; }