cl-parser: Implement a module inlining pass
This commit is contained in:
		
							
								
								
									
										102
									
								
								compiler/cl-parser/src/inliner.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								compiler/cl-parser/src/inliner.rs
									
									
									
									
									
										Normal file
									
								
							@@ -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<Path>) -> 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<File, (File, IoErrs, ParseErrs)> {
 | 
			
		||||
        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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -14,3 +14,5 @@ use cl_token::*;
 | 
			
		||||
pub mod error;
 | 
			
		||||
 | 
			
		||||
pub mod parser;
 | 
			
		||||
 | 
			
		||||
pub mod inliner;
 | 
			
		||||
 
 | 
			
		||||
@@ -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<dyn Error>> {
 | 
			
		||||
            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<dyn Error>> {
 | 
			
		||||
    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<usize>) {
 | 
			
		||||
    println!("\x1b[90m{module}\x1b[0m");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn inline_modules(code: cl_ast::File, path: impl AsRef<path::Path>) -> 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<dyn Error>> {
 | 
			
		||||
    println!("\x1b[H\x1b[2J");
 | 
			
		||||
    banner();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										105
									
								
								stdlib/lib.cl
									
									
									
									
									
								
							
							
						
						
									
										105
									
								
								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;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								stdlib/num.cl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								stdlib/num.cl
									
									
									
									
									
										Normal file
									
								
							@@ -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()
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										66
									
								
								stdlib/test.cl
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								stdlib/test.cl
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
		Reference in New Issue
	
	Block a user