From 9c3c2e86740b1c5d9e2f4336bc09a2343dc4051f Mon Sep 17 00:00:00 2001 From: John Date: Sun, 21 Apr 2024 22:31:01 -0500 Subject: [PATCH] cl-parser: Implement a module inlining pass --- compiler/cl-parser/src/inliner.rs | 102 +++++++++++++++++++++++++++ compiler/cl-parser/src/lib.rs | 2 + compiler/cl-repl/examples/typeck.rs | 22 +++++- stdlib/lib.cl | 105 ++-------------------------- stdlib/num.cl | 41 +++++++++++ stdlib/test.cl | 66 +++++++++++++++++ 6 files changed, 235 insertions(+), 103 deletions(-) create mode 100644 compiler/cl-parser/src/inliner.rs create mode 100644 stdlib/num.cl create mode 100644 stdlib/test.cl diff --git a/compiler/cl-parser/src/inliner.rs b/compiler/cl-parser/src/inliner.rs new file mode 100644 index 0000000..f699cfd --- /dev/null +++ b/compiler/cl-parser/src/inliner.rs @@ -0,0 +1,102 @@ +//! The [ModuleInliner] reads files described in the module structure of the + +use crate::Parser; +use cl_ast::{ast_visitor::Fold, *}; +use cl_lexer::Lexer; +use std::{ + ops::{Deref, DerefMut}, + path::{Path, PathBuf}, +}; + +type IoErrs = Vec<(PathBuf, std::io::Error)>; +type ParseErrs = Vec<(PathBuf, crate::error::Error)>; + +pub struct ModuleInliner { + path: PathBuf, + io_errs: IoErrs, + parse_errs: ParseErrs, +} + +impl ModuleInliner { + /// Creates a new [ModuleInliner] + pub fn new(root: impl AsRef) -> Self { + Self { + path: root.as_ref().to_path_buf(), + io_errs: Default::default(), + parse_errs: Default::default(), + } + } + 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)) + } + } + /// 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 { + self.io_errs.push((self.path.clone(), error)); + Module { name, kind: 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 { + self.parse_errs.push((self.path.clone(), error)); + Module { name, kind: ModuleKind::Outline } + } +} + +impl Fold for ModuleInliner { + fn fold_module(&mut self, m: Module) -> cl_ast::Module { + let Module { name, kind } = m; + let mut inliner = PathMiser::new(self, &name.0); + + let kind = inliner.fold_module_kind(kind); + + let ModuleKind::Outline = kind else { + return 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, + }; + + let kind = match Parser::new(Lexer::new(&file)).file() { + Err(e) => return inliner.parse_oops(name, e), + Ok(file) => ModuleKind::Inline(file), + }; + + 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(); + } +} diff --git a/compiler/cl-parser/src/lib.rs b/compiler/cl-parser/src/lib.rs index 4c735ef..067241e 100644 --- a/compiler/cl-parser/src/lib.rs +++ b/compiler/cl-parser/src/lib.rs @@ -14,3 +14,5 @@ use cl_token::*; pub mod error; pub mod parser; + +pub mod inliner; diff --git a/compiler/cl-repl/examples/typeck.rs b/compiler/cl-repl/examples/typeck.rs index d2c1fdb..daab845 100644 --- a/compiler/cl-repl/examples/typeck.rs +++ b/compiler/cl-repl/examples/typeck.rs @@ -3,12 +3,12 @@ use cl_ast::{ desugar::{squash_groups::SquashGroups, while_else::WhileElseDesugar}, }; use cl_lexer::Lexer; -use cl_parser::Parser; +use cl_parser::{inliner::ModuleInliner, Parser}; use cl_typeck::{ definition::Def, name_collector::NameCollectable, project::Project, type_resolver::resolve, }; use repline::{error::Error as RlError, prebaked::*}; -use std::error::Error; +use std::{error::Error, path}; // Path to display in standard library errors const STDLIB_DISPLAY_PATH: &str = "stdlib/lib.cl"; @@ -37,6 +37,7 @@ fn main() -> Result<(), Box> { Err(e)? } }; + let code = inline_modules(code, concat!(env!("PWD"), "/stdlib")); unsafe { TREES.push(code) }.collect_in_root(&mut prj)?; @@ -80,6 +81,7 @@ fn enter_code(prj: &mut Project) -> Result<(), RlError> { return Ok(Response::Break); } let code = Parser::new(Lexer::new(line)).file()?; + let code = inline_modules(code, ""); let code = WhileElseDesugar.fold_file(code); // Safety: this is totally unsafe unsafe { TREES.push(code) }.collect_in_root(prj)?; @@ -117,6 +119,7 @@ fn query_type_expression(prj: &mut Project) -> Result<(), RlError> { } fn resolve_all(prj: &mut Project) -> Result<(), Box> { + prj.resolve_imports()?; for id in prj.pool.key_iter() { resolve(prj, id)?; } @@ -148,6 +151,21 @@ fn pretty_def(def: &Def, id: impl Into) { println!("\x1b[90m{module}\x1b[0m"); } +fn inline_modules(code: cl_ast::File, path: impl AsRef) -> cl_ast::File { + match ModuleInliner::new(path).inline(code) { + Err((code, io, parse)) => { + for (file, error) in io { + eprintln!("{}:{error}", file.display()); + } + for (file, error) in parse { + eprintln!("{}:{error}", file.display()); + } + code + } + Ok(code) => code, + } +} + fn clear() -> Result<(), Box> { println!("\x1b[H\x1b[2J"); banner(); diff --git a/stdlib/lib.cl b/stdlib/lib.cl index c866407..2aafa77 100644 --- a/stdlib/lib.cl +++ b/stdlib/lib.cl @@ -1,107 +1,10 @@ //! # The Conlang Standard Library -#[intrinsic = "bool"] -pub type bool; - -#[intrinsic = "char"] -pub type char; - -#[intrinsic = "i8"] -pub type i8; - -#[intrinsic = "i16"] -pub type i16; - -#[intrinsic = "i32"] -pub type i32; - -#[intrinsic = "i64"] -pub type i64; - -#[intrinsic = "u8"] -pub type u8; - -#[intrinsic = "u16"] -pub type u16; - -#[intrinsic = "u32"] -pub type u32; - -#[intrinsic = "u64"] -pub type u64; - -impl bool { - const MIN: Self = false; - const MAX: Self = { - fn ret_true() -> Self { - true - } - ret_true() - }; +pub mod preamble { + pub use super::num::*; } -fn if_else() -> i32 { - // block 1 - let x = 10; - let y = x + 2; - - if x > 10 - // compare x and 10 - // end block 1, goto block 2 else goto block 3 - { - // block 2 - 4 - // end block 2, goto block 4 - } else { - // block 3 - 5 - // end block 3, goto block 4 - } - // block 4 -} +pub mod num; #[cfg("test")] -mod test { - //! Tests for funky behavior - - struct UnitLike; - - struct TupleLike(super::i32, super::self::test::super::char) - - struct StructLike { - pub member1: UnitLike, - member2: TupleLike, - } - - enum NeverLike; - - enum EmptyLike { - Empty, - } - - enum Hodgepodge { - Empty, - UnitLike (), - Tuple (TupleLike), - StructEmpty {}, - StructLike { member1: UnitLike, member2: TupleLike }, - } - - fn noop () -> bool { - loop if false { - - } else break loop if false { - - } else break loop if false { - - } else break true; - } - - fn while_else() -> i32 { - while conditional { - pass - } else { - fail - } - } -} +mod test; diff --git a/stdlib/num.cl b/stdlib/num.cl new file mode 100644 index 0000000..e93ee37 --- /dev/null +++ b/stdlib/num.cl @@ -0,0 +1,41 @@ +//! The primitive numeric types + +#[intrinsic = "bool"] +pub type bool; + +#[intrinsic = "char"] +pub type char; + +#[intrinsic = "i8"] +pub type i8; + +#[intrinsic = "i16"] +pub type i16; + +#[intrinsic = "i32"] +pub type i32; + +#[intrinsic = "i64"] +pub type i64; + +#[intrinsic = "u8"] +pub type u8; + +#[intrinsic = "u16"] +pub type u16; + +#[intrinsic = "u32"] +pub type u32; + +#[intrinsic = "u64"] +pub type u64; + +impl bool { + const MIN: Self = false; + const MAX: Self = { + fn ret_true() -> Self { + true + } + ret_true() + }; +} diff --git a/stdlib/test.cl b/stdlib/test.cl new file mode 100644 index 0000000..2faad3e --- /dev/null +++ b/stdlib/test.cl @@ -0,0 +1,66 @@ + +//! Tests for funky behavior +use super::preamble::*; + +struct UnitLike; + +struct TupleLike(super::i32, super::self::test::char) + +struct StructLike { + pub member1: UnitLike, + member2: TupleLike, +} + +enum NeverLike; + +enum EmptyLike { + Empty, +} + +enum Hodgepodge { + Empty, + UnitLike (), + Tuple (TupleLike), + StructEmpty {}, + StructLike { member1: UnitLike, member2: TupleLike }, +} + +fn noop () -> bool { + loop if false { + + } else break loop if false { + + } else break loop if false { + + } else break true; +} + +fn while_else() -> i32 { + while conditional { + pass + } else { + fail + } +} + +fn if_else() -> i32 { + // block 1 + let x = 10; + let y = x + 2; + + if x > 10 + // compare x and 10 + // end block 1, goto block 2 else goto block 3 + { + // block 2 + 4 + // end block 2, goto block 4 + } else { + // block 3 + 5 + // end block 3, goto block 4 + } + // block 4 +} + +let x = 0;