From f150dd6c614988241f8667784d2f4864836aa597 Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Wed, 13 Mar 2024 12:06:33 +0100 Subject: [PATCH] feat: Module file declarations, fixes --- edb/src/main.rs | 68 ++++++++++++++++-------- lib/edlang_ast/src/lib.rs | 2 + lib/edlang_codegen_llvm/src/codegen.rs | 33 ++++++------ lib/edlang_driver/src/lib.rs | 71 ++++++++++++++------------ lib/edlang_ir/src/lib.rs | 16 +++--- lib/edlang_lowering/src/lib.rs | 3 +- lib/edlang_parser/src/grammar.lalrpop | 13 ++++- lib/edlang_parser/src/tokens.rs | 2 + 8 files changed, 127 insertions(+), 81 deletions(-) diff --git a/edb/src/main.rs b/edb/src/main.rs index b925ca7b7..d5b6ce01c 100644 --- a/edb/src/main.rs +++ b/edb/src/main.rs @@ -114,7 +114,7 @@ fn main() -> Result<()> { if bin { std::fs::write( path.join("src").join("main.ed"), - r#"pub fn main() -> i32 {{ + r#"pub fn main() -> i32 { return 0; }"#, )?; @@ -123,7 +123,7 @@ fn main() -> Result<()> { if lib { std::fs::write( path.join("src").join("lib.ed"), - r#"pub fn main() -> i32 {{ + r#"pub fn main() -> i32 { return 0; }"#, )?; @@ -201,7 +201,6 @@ fn main() -> Result<()> { std::fs::create_dir_all(&target_dir)?; } - let has_main = src_dir.join("main.ed").exists(); let output = target_dir.join(config.package.name); let (profile, profile_name) = if let Some(profile) = profile { @@ -230,27 +229,44 @@ fn main() -> Result<()> { ) }; - let compile_args = CompilerArgs { - input: src_dir, - output: output.clone(), - release, - optlevel: Some(profile.opt_level), - debug_info: Some(profile.debug_info), - library: !has_main, - ast: false, - ir: false, - llvm: true, - asm: false, - object: true, - }; + let lib_ed = src_dir.join("lib.ed"); + let main_ed = src_dir.join("main.ed"); let start = Instant::now(); - let object = compile(&compile_args)?; - if !has_main { - link_shared_lib(&[object], &output)?; - } else { - link_binary(&[object], &output)?; + for file in [main_ed, lib_ed] { + if file.exists() { + let is_lib = file.file_stem().unwrap() == "lib"; + + let compile_args = CompilerArgs { + input: file, + output: if is_lib { + let name = output.file_stem().unwrap().to_string_lossy().to_string(); + let name = format!("lib{name}"); + output + .with_file_name(name) + .with_extension(get_platform_library_ext()) + } else { + output.clone() + }, + release, + optlevel: Some(profile.opt_level), + debug_info: Some(profile.debug_info), + library: is_lib, + ast: false, + ir: false, + llvm: true, + asm: false, + object: true, + }; + let object = compile(&compile_args)?; + + if compile_args.library { + link_shared_lib(&[object], &compile_args.output)?; + } else { + link_binary(&[object], &compile_args.output)?; + } + } } let elapsed = start.elapsed(); @@ -280,3 +296,13 @@ fn main() -> Result<()> { Ok(()) } + +pub fn get_platform_library_ext() -> &'static str { + if cfg!(target_os = "macos") { + "dylib" + } else if cfg!(target_os = "windows") { + "dll" + } else { + "so" + } +} diff --git a/lib/edlang_ast/src/lib.rs b/lib/edlang_ast/src/lib.rs index f28de1556..a533fc121 100644 --- a/lib/edlang_ast/src/lib.rs +++ b/lib/edlang_ast/src/lib.rs @@ -6,6 +6,7 @@ pub use edlang_span::Span; pub struct Module { pub name: Ident, pub imports: Vec, + pub external_modules: Vec, pub contents: Vec, pub span: Span, } @@ -138,6 +139,7 @@ pub struct Function { pub name: Ident, pub is_extern: bool, pub is_public: bool, + pub is_exported: bool, pub params: Vec, pub return_type: Option, pub body: Option, diff --git a/lib/edlang_codegen_llvm/src/codegen.rs b/lib/edlang_codegen_llvm/src/codegen.rs index fcb9c2ba9..41d76cac7 100644 --- a/lib/edlang_codegen_llvm/src/codegen.rs +++ b/lib/edlang_codegen_llvm/src/codegen.rs @@ -247,7 +247,7 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit let fn_value = ctx.module.add_function( &body.get_mangled_name(), fn_type, - Some(if body.is_pub || body.is_extern { + Some(if body.is_extern || body.is_exported { inkwell::module::Linkage::External } else { inkwell::module::Linkage::Private @@ -272,20 +272,23 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit DIFlagsConstants::PRIVATE }, ); - let subprogram = ctx.di_builder.create_function( - ctx.di_namespace, - &body.name, - Some(&body.get_mangled_name()), - ctx.di_unit.get_file(), - line as u32 + 1, - di_type, - !body.is_pub, - is_definition, - line as u32 + 1, - 0, - false, - ); - fn_value.set_subprogram(subprogram); + + if fn_value.get_subprogram().is_none() { + let subprogram = ctx.di_builder.create_function( + ctx.di_namespace, + &body.name, + Some(&body.get_mangled_name()), + ctx.di_unit.get_file(), + line as u32 + 1, + di_type, + body.is_exported || body.is_extern, + is_definition && !body.is_extern, + line as u32 + 1, + 0, + false, + ); + fn_value.set_subprogram(subprogram); + } } fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> { diff --git a/lib/edlang_driver/src/lib.rs b/lib/edlang_driver/src/lib.rs index ed236fe46..1bbf050c8 100644 --- a/lib/edlang_driver/src/lib.rs +++ b/lib/edlang_driver/src/lib.rs @@ -3,9 +3,9 @@ use std::{path::PathBuf, time::Instant}; use anyhow::Result; use ariadne::{sources, Source}; use clap::Parser; +use edlang_ast::Module; use edlang_lowering::lower_modules; use edlang_session::{DebugInfo, OptLevel, Session}; -use walkdir::WalkDir; use crate::linker::{link_binary, link_shared_lib}; @@ -77,44 +77,48 @@ pub fn main() -> Result<()> { Ok(()) } -pub fn compile(args: &CompilerArgs) -> Result { - let mut files = Vec::new(); - for entry in WalkDir::new(&args.input).sort_by_file_name() { - let entry = entry?; - if let Some(ext) = entry.path().extension() { - if ext.eq_ignore_ascii_case("ed") { - files.push(entry.path().to_path_buf()); - } +pub fn parse_file(modules: &mut Vec<(PathBuf, String, Module)>, mut path: PathBuf) -> Result<()> { + if path.is_dir() { + path = path.join("mod.ed"); + } + + let source = std::fs::read_to_string(&path)?; + + let module_ast = edlang_parser::parse_ast( + &source, + &path.file_stem().expect("no file stem").to_string_lossy(), + ); + + let module_temp = match module_ast { + Ok(module) => module, + Err(error) => { + let path = path.display().to_string(); + let report = edlang_parser::error_to_report(&path, &error)?; + edlang_parser::print_report(&path, &source, report)?; + std::process::exit(1) } + }; + + for ident in &module_temp.external_modules { + let module_path = path + .parent() + .unwrap() + .join(&ident.name) + .with_extension("ed"); + // todo: fancy error if doesnt exist? + parse_file(modules, module_path)?; } - if files.is_empty() { - panic!("files is empty"); - } + modules.push((path, source, module_temp)); + Ok(()) +} + +pub fn compile(args: &CompilerArgs) -> Result { let start_time = Instant::now(); let mut modules = Vec::new(); - - for path in files { - let source = std::fs::read_to_string(&path)?; - - let module_ast = edlang_parser::parse_ast( - &source, - &path.file_stem().expect("no file stem").to_string_lossy(), - ); - - let module_temp = match module_ast { - Ok(module) => module, - Err(error) => { - let path = path.display().to_string(); - let report = edlang_parser::error_to_report(&path, &error)?; - edlang_parser::print_report(&path, &source, report)?; - std::process::exit(1) - } - }; - modules.push((path, source, module_temp)); - } + parse_file(&mut modules, args.input.clone())?; let session = Session { file_paths: modules.iter().map(|x| x.0.clone()).collect(), @@ -181,7 +185,8 @@ pub fn compile(args: &CompilerArgs) -> Result { )?; } - let object_path = edlang_codegen_llvm::compile(&session, &program_ir).unwrap(); + let object_path = + edlang_codegen_llvm::compile(&session, &program_ir).expect("failed to compile"); let elapsed = start_time.elapsed(); tracing::debug!("Done in {:?}", elapsed); diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 42e0a558f..bb9fdbdd0 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -70,6 +70,8 @@ pub struct Body { pub def_id: DefId, pub is_pub: bool, pub is_extern: bool, + // exported means externally available in a shared library or as main + pub is_exported: bool, pub name: String, pub locals: SmallVec<[Local; 4]>, pub blocks: SmallVec<[BasicBlock; 8]>, @@ -94,18 +96,14 @@ impl Body { } pub fn get_mangled_name(&self) -> String { - if self.is_extern { + if self.is_extern || self.is_exported { return self.name.clone(); } - if self.name == "main" { - "main".to_string() - } else { - format!( - "{}@{}@{}", - self.name, self.def_id.program_id, self.def_id.id - ) - } + format!( + "{}@{}@{}", + self.name, self.def_id.program_id, self.def_id.id + ) } } diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index 762b3b69a..bed9ffd4e 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -149,8 +149,9 @@ fn lower_function( let body = ctx.body.modules.get(&module_id).unwrap(); *body.symbols.functions.get(&func.name.name).unwrap() }, - is_pub: func.is_public || func.name.name == "main", + is_pub: func.is_public, is_extern: func.is_extern, + is_exported: func.is_exported || func.name.name == "main", fn_span: func.span, }, local_module: module_id, diff --git a/lib/edlang_parser/src/grammar.lalrpop b/lib/edlang_parser/src/grammar.lalrpop index d26e9a23f..0d07b1578 100644 --- a/lib/edlang_parser/src/grammar.lalrpop +++ b/lib/edlang_parser/src/grammar.lalrpop @@ -28,6 +28,7 @@ extern { "in" => Token::KeywordIn, "extern" => Token::KeywordExtern, "as" => Token::KeywordAs, + "exported" => Token::KeywordExported, // literals "identifier" => Token::Identifier(), @@ -426,16 +427,18 @@ pub(crate) Function: ast::Function = { " )?> ";" => ast::Function { is_public: is_public.is_some(), is_extern: true, + is_exported: false, name, params, return_type, body: None, span: ast::Span::new(lo, hi), }, - "fn" "(" > ")" + "fn" "(" > ")" " )?> => ast::Function { is_public: is_public.is_some(), is_extern: false, + is_exported: is_exported.is_some(), name, params, return_type, @@ -478,13 +481,18 @@ pub(crate) Import: ast::Import = { } } +pub(crate) ExternalModule: ast::Ident = { + "mod" ";" => name +} + pub TopLevelModule: ast::Module = { - ?> > => ast::Module { + ?> ?> > => ast::Module { name: ast::Ident { name: module_name.to_string(), span: ast::Span::new(0, 0), }, imports: imports.unwrap_or(vec![]), + external_modules: external_modules.unwrap_or(vec![]), contents, span: ast::Span::new(lo, hi), } @@ -494,6 +502,7 @@ pub Module: ast::Module = { "mod" "{" ?> > "}" => ast::Module { name, imports: imports.unwrap_or(vec![]), + external_modules: Vec::new(), contents, span: ast::Span::new(lo, hi), } diff --git a/lib/edlang_parser/src/tokens.rs b/lib/edlang_parser/src/tokens.rs index d7caa54f8..880e8b160 100644 --- a/lib/edlang_parser/src/tokens.rs +++ b/lib/edlang_parser/src/tokens.rs @@ -57,6 +57,8 @@ pub enum Token { KeywordExtern, #[token("as")] KeywordAs, + #[token("exported")] + KeywordExported, // Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/ #[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())]