diff --git a/compiler/cl-parser/src/inliner.rs b/compiler/cl-parser/src/inliner.rs index f699cfd..b478cfc 100644 --- a/compiler/cl-parser/src/inliner.rs +++ b/compiler/cl-parser/src/inliner.rs @@ -3,10 +3,7 @@ use crate::Parser; use cl_ast::{ast_visitor::Fold, *}; use cl_lexer::Lexer; -use std::{ - ops::{Deref, DerefMut}, - path::{Path, PathBuf}, -}; +use std::path::{Path, PathBuf}; type IoErrs = Vec<(PathBuf, std::io::Error)>; type ParseErrs = Vec<(PathBuf, crate::error::Error)>; @@ -26,77 +23,76 @@ impl ModuleInliner { parse_errs: Default::default(), } } + + /// Returns true when the [ModuleInliner] has errors to report + pub fn has_errors(&self) -> bool { + !(self.io_errs.is_empty() && self.parse_errs.is_empty()) + } + + /// Returns the [IO Errors](IoErrs) and [parse Errors](ParseErrs) + pub fn into_errs(self) -> Option<(IoErrs, ParseErrs)> { + self.has_errors().then_some((self.io_errs, self.parse_errs)) + } + + /// Traverses a [File], attempting to inline all submodules. + /// + /// This is a simple wrapper around [ModuleInliner::fold_file()] and + /// [ModuleInliner::into_errs()] pub fn inline(mut self, file: File) -> Result { let file = self.fold_file(file); - if self.io_errs.is_empty() && self.parse_errs.is_empty() { - Ok(file) - } else { - Err((file, self.io_errs, self.parse_errs)) + match self.into_errs() { + Some((io, parse)) => Err((file, io, parse)), + None => Ok(file), } } - /// Records an [I/O error](std::io::Error), and rebuilds the outline [Module] - fn io_oops(&mut self, name: Identifier, error: std::io::Error) -> Module { + + /// Records an [I/O error](std::io::Error) for later + fn handle_io_error(&mut self, error: std::io::Error) -> ModuleKind { self.io_errs.push((self.path.clone(), error)); - Module { name, kind: ModuleKind::Outline } + ModuleKind::Outline } - /// Records a [parse error](crate::error::Error), and rebuilds the outline [Module] - fn parse_oops(&mut self, name: Identifier, error: crate::error::Error) -> Module { + + /// Records a [parse error](crate::error::Error) for later + fn handle_parse_error(&mut self, error: crate::error::Error) -> ModuleKind { self.parse_errs.push((self.path.clone(), error)); - Module { name, kind: ModuleKind::Outline } + ModuleKind::Outline } } impl Fold for ModuleInliner { - fn fold_module(&mut self, m: Module) -> cl_ast::Module { + /// Traverses down the module tree, entering ever nested directories + fn fold_module(&mut self, m: Module) -> Module { let Module { name, kind } = m; - let mut inliner = PathMiser::new(self, &name.0); + self.path.push(&name.0); // cd ./name - let kind = inliner.fold_module_kind(kind); + let kind = self.fold_module_kind(kind); - let ModuleKind::Outline = kind else { - return Module { name, kind }; - }; + self.path.pop(); // cd .. + Module { name, kind } + } - inliner.path.set_extension("cl"); - let file = match std::fs::read_to_string(&inliner.path) { - Err(e) => return inliner.io_oops(name, e), - Ok(s) => s, + /// Attempts to read and parse a file for every module in the tree + fn fold_module_kind(&mut self, m: ModuleKind) -> ModuleKind { + if let ModuleKind::Inline(f) = m { + return ModuleKind::Inline(self.fold_file(f)); + } + // cd path/mod.cl + self.path.set_extension("cl"); + + let file = match std::fs::read_to_string(&self.path) { + Err(error) => return self.handle_io_error(error), + Ok(file) => file, }; let kind = match Parser::new(Lexer::new(&file)).file() { - Err(e) => return inliner.parse_oops(name, e), + Err(e) => return self.handle_parse_error(e), Ok(file) => ModuleKind::Inline(file), }; + // cd path/mod + self.path.set_extension(""); - inliner.path.set_extension(""); - Module { name, kind: inliner.fold_module_kind(kind) } - } -} - -/// I am the [Path] miser. Whatever I touch turns to [Path] in my clutch. -pub struct PathMiser<'mi> { - mi: &'mi mut ModuleInliner, -} -impl<'mi> PathMiser<'mi> { - pub fn new(mi: &'mi mut ModuleInliner, name: &str) -> Self { - mi.path.push(name); - Self { mi } - } -} -impl<'mi> Deref for PathMiser<'mi> { - type Target = ModuleInliner; - fn deref(&self) -> &Self::Target { - self.mi - } -} -impl<'mi> DerefMut for PathMiser<'mi> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.mi - } -} -impl<'mi> Drop for PathMiser<'mi> { - fn drop(&mut self) { - self.mi.path.pop(); + // The newly loaded module may need further inlining + self.fold_module_kind(kind) } }