From bd3c4da7ea1815064f888c0da71d706b753ac77d Mon Sep 17 00:00:00 2001 From: Edgar Luque Date: Mon, 11 Mar 2024 12:02:14 +0100 Subject: [PATCH] feat: usable edb --- Cargo.lock | 8 + edb/Cargo.toml | 1 + edb/src/main.rs | 154 ++++++++++++++- lib/edlang_check/src/lib.rs | 180 +++++++++++------- lib/edlang_codegen_llvm/src/codegen.rs | 48 +++-- lib/edlang_codegen_llvm/src/lib.rs | 1 - lib/edlang_driver/Cargo.toml | 1 + lib/edlang_driver/src/lib.rs | 141 ++++++++------ .../src/linker.rs | 80 ++++---- lib/edlang_driver/tests/common.rs | 30 ++- lib/edlang_ir/src/lib.rs | 1 + lib/edlang_lowering/src/common.rs | 1 + lib/edlang_lowering/src/errors.rs | 39 +++- lib/edlang_lowering/src/lib.rs | 39 ++-- lib/edlang_lowering/src/prepass.rs | 28 ++- lib/edlang_session/src/lib.rs | 6 +- 16 files changed, 528 insertions(+), 230 deletions(-) rename lib/{edlang_codegen_llvm => edlang_driver}/src/linker.rs (61%) diff --git a/Cargo.lock b/Cargo.lock index 74509ce34..1b14525f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,6 +241,7 @@ dependencies = [ "clap", "edlang_driver", "git2", + "owo-colors", "serde", "toml", ] @@ -294,6 +295,7 @@ dependencies = [ "test-case", "tracing", "tracing-subscriber", + "walkdir", ] [[package]] @@ -785,6 +787,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "owo-colors" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caff54706df99d2a78a5a4e3455ff45448d81ef1bb63c22cd14052ca0e993a3f" + [[package]] name = "parking_lot" version = "0.12.1" diff --git a/edb/Cargo.toml b/edb/Cargo.toml index 872f63762..5dcf2b2d7 100644 --- a/edb/Cargo.toml +++ b/edb/Cargo.toml @@ -20,3 +20,4 @@ clap = { version = "4.4.16", features = ["derive"] } toml = "0.8.10" serde = { version = "1.0.197", features = ["derive"] } git2 = "0.18.2" +owo-colors = "4.0.0" diff --git a/edb/src/main.rs b/edb/src/main.rs index bc4614a49..f23bd269b 100644 --- a/edb/src/main.rs +++ b/edb/src/main.rs @@ -1,9 +1,15 @@ -use std::{collections::HashMap, path::PathBuf}; +use std::{collections::HashMap, fs::File, io::Read, path::PathBuf, time::Instant}; -use anyhow::{Context, Result}; +use anyhow::{bail, Context, Result}; use clap::{Parser, Subcommand}; use config::{Config, Package, Profile}; -use git2::{IndexAddOption, Repository, RepositoryInitOptions}; +use edlang_driver::{ + compile, + linker::{link_binary, link_shared_lib}, + CompilerArgs, +}; +use git2::{IndexAddOption, Repository}; +use owo_colors::OwoColorize; mod config; @@ -36,6 +42,10 @@ enum Commands { /// Build for release with all optimizations. #[arg(short, long, default_value_t = false)] release: bool, + + /// Override the profile to use. + #[arg(short, long)] + profile: Option, }, } @@ -131,13 +141,13 @@ mod {} {{ } { - let mut repo = Repository::init(&path).context("failed to create repository")?; + let repo = Repository::init(&path).context("failed to create repository")?; let sig = repo.signature()?; let tree_id = { let mut index = repo.index()?; - index.add_all(["*"], IndexAddOption::DEFAULT, None)?; - + index.add_all(["."].iter(), IndexAddOption::DEFAULT, None)?; + index.write()?; index.write_tree()? }; @@ -145,8 +155,138 @@ mod {} {{ repo.commit(Some("HEAD"), &sig, &sig, "Initial commit", &tree, &[]) .context("failed to create initial commit")?; } + + if bin { + println!( + " {} binary (application) `{}` package", + "Created".green().bold(), + name + ); + } else { + println!(" {} library `{}` package", "Created".green(), name); + } + } + Commands::Build { release, profile } => { + let mut current_dir = std::env::current_dir()?; + let mut config_path = None; + + for _ in 0..3 { + if !current_dir.join("Ed.toml").exists() { + current_dir = if let Some(parent) = current_dir.parent() { + parent.to_path_buf() + } else { + bail!("Couldn't find Ed.toml"); + }; + } else { + config_path = Some(current_dir.join("Ed.toml")); + break; + } + } + + let config_path = match config_path { + Some(x) => x, + None => bail!("Couldn't find Ed.toml"), + }; + + let base_dir = config_path + .parent() + .context("couldn't get config parent dir")?; + let mut config = File::open(&config_path).context("Failed to open Ed.toml")?; + let mut buf = String::new(); + config.read_to_string(&mut buf)?; + + let config: Config = toml::from_str(&buf).context("failed to parse Ed.toml")?; + + println!( + " {} {} v{} ({})", + "Compiling".green().bold(), + config.package.name, + config.package.version, + base_dir.display() + ); + + let src_dir = base_dir.join("src"); + let target_dir = base_dir.join("target-ed"); + + if !target_dir.exists() { + 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 { + ( + config + .profile + .get(&profile) + .context("Couldn't get requested profile")?, + profile, + ) + } else if release { + ( + config + .profile + .get("release") + .context("Couldn't get profile: release")?, + "release".to_string(), + ) + } else { + ( + config + .profile + .get("dev") + .context("Couldn't get profile: dev")?, + "dev".to_string(), + ) + }; + + 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 start = Instant::now(); + let object = compile(&compile_args)?; + + if has_main { + link_shared_lib(&[object], &output)?; + } else { + link_binary(&[object], &output)?; + } + + let elapsed = start.elapsed(); + + println!( + " {} {} [{}{}] in {elapsed:?}", + "Finished".green().bold(), + profile_name, + if profile.opt_level > 0 { + "optimized" + } else { + "unoptimized" + }, + if profile.debug_info { + " + debuginfo" + } else { + "" + } + ); + /* + + Finished dev [unoptimized + debuginfo] target(s) in 0.06s + Running `/data2/edgar/edlang/target/debug/edb build` + */ } - Commands::Build { release } => todo!(), } Ok(()) diff --git a/lib/edlang_check/src/lib.rs b/lib/edlang_check/src/lib.rs index ecfa1e477..8e6b3223c 100644 --- a/lib/edlang_check/src/lib.rs +++ b/lib/edlang_check/src/lib.rs @@ -9,11 +9,15 @@ pub fn lowering_error_to_report( error: LoweringError, session: &Session, ) -> Report<(String, Range)> { - let path = session.file_path.display().to_string(); let mut colors = ColorGenerator::new(); colors.next(); match error { - LoweringError::ModuleNotFound { span, module } => { + LoweringError::ModuleNotFound { + span, + module, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); let offset = span.lo; Report::build(ReportKind::Error, path.clone(), offset) .with_code("E1") @@ -25,55 +29,79 @@ pub fn lowering_error_to_report( .with_message("Unresolved import.") .finish() } - LoweringError::FunctionNotFound { span, function } => { + LoweringError::FunctionNotFound { + span, + function, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); Report::build(ReportKind::Error, path.clone(), span.lo) - .with_code("EFNNOTFOUND") - .with_label( - Label::new((path, span.into())) - .with_message(format!("Function {function:?} not found.")) - .with_color(colors.next()), - ) - .finish() - }, - LoweringError::ImportNotFound { import_span, module_span, symbol } => { - let offset = symbol.span.lo; - Report::build(ReportKind::Error, path.clone(), offset) - .with_code("E2") - .with_label( - Label::new((path.clone(), module_span.into())) - .with_message("In module this module."), - ) - .with_label( - Label::new((path.clone(), import_span.into())) - .with_message("In this import statement"), - ) - .with_label( - Label::new((path, symbol.span.into())) - .with_message(format!("Failed to find symbol {:?}", symbol.name)) - .with_color(colors.next()), - ) - .with_message("Unresolved import.") - .finish() - }, - LoweringError::BorrowNotMutable { span, name, type_span } => { - let mut labels = vec![ - Label::new((path.clone(), span.into())) - .with_message(format!("Can't mutate {name:?} because it's behind a immutable borrow")) - .with_color(colors.next()) - ]; + .with_code("EFNNOTFOUND") + .with_label( + Label::new((path, span.into())) + .with_message(format!("Function {function:?} not found.")) + .with_color(colors.next()), + ) + .finish() + } + LoweringError::ImportNotFound { + import_span, + module_span, + symbol, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); + let offset = symbol.span.lo; + Report::build(ReportKind::Error, path.clone(), offset) + .with_code("E2") + .with_label( + Label::new((path.clone(), module_span.into())) + .with_message("In module this module."), + ) + .with_label( + Label::new((path.clone(), import_span.into())) + .with_message("In this import statement"), + ) + .with_label( + Label::new((path, symbol.span.into())) + .with_message(format!("Failed to find symbol {:?}", symbol.name)) + .with_color(colors.next()), + ) + .with_message("Unresolved import.") + .finish() + } + LoweringError::BorrowNotMutable { + span, + name, + type_span, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); + let mut labels = vec![Label::new((path.clone(), span.into())) + .with_message(format!( + "Can't mutate {name:?} because it's behind a immutable borrow" + )) + .with_color(colors.next())]; if let Some(type_span) = type_span { - labels.push(Label::new((path.clone(), type_span.into())) - .with_message(format!("Variable {name:?} has this type")) - .with_color(colors.next())); + labels.push( + Label::new((path.clone(), type_span.into())) + .with_message(format!("Variable {name:?} has this type")) + .with_color(colors.next()), + ); } Report::build(ReportKind::Error, path.clone(), span.lo) - .with_code("EREFMUT") - .with_labels(labels) - .finish() - }, - LoweringError::UnrecognizedType { span, name } => { + .with_code("EREFMUT") + .with_labels(labels) + .finish() + } + LoweringError::UnrecognizedType { + span, + name, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); Report::build(ReportKind::Error, path.clone(), span.lo) .with_code("E3") .with_label( @@ -83,19 +111,26 @@ pub fn lowering_error_to_report( ) .with_message(format!("Unresolved type {:?}.", name)) .finish() - }, - LoweringError::UnexpectedType { span, found, expected } => { - let mut labels = vec![ - Label::new((path.clone(), span.into())) - .with_message(format!("Unexpected type '{}', expected '{}'", found, expected.kind)) - .with_color(colors.next()) - ]; + } + LoweringError::UnexpectedType { + span, + found, + expected, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); + let mut labels = vec![Label::new((path.clone(), span.into())) + .with_message(format!( + "Unexpected type '{}', expected '{}'", + found, expected.kind + )) + .with_color(colors.next())]; if let Some(span) = expected.span { labels.push( Label::new((path.clone(), span.into())) .with_message(format!("expected '{}' due to this type", expected.kind)) - .with_color(colors.next()) + .with_color(colors.next()), ); } @@ -104,8 +139,9 @@ pub fn lowering_error_to_report( .with_labels(labels) .with_message(format!("expected type {}.", expected.kind)) .finish() - }, - LoweringError::IdNotFound { span, id } => { + } + LoweringError::IdNotFound { span, id, file_id } => { + let path = session.file_paths[file_id].display().to_string(); Report::build(ReportKind::Error, path.clone(), span.lo) .with_code("E_ID") .with_label( @@ -115,8 +151,13 @@ pub fn lowering_error_to_report( ) .with_message(format!("Failed to find definition id {id:?}, this is most likely a compiler bug or a unimplemented lowering")) .finish() - }, - LoweringError::NotYetImplemented { span, message } => { + } + LoweringError::NotYetImplemented { + span, + message, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); Report::build(ReportKind::Error, path.clone(), span.lo) .with_code("TODO") .with_label( @@ -125,16 +166,21 @@ pub fn lowering_error_to_report( .with_color(colors.next()), ) .finish() - }, - LoweringError::UseOfUndeclaredVariable { span, name } => { + } + LoweringError::UseOfUndeclaredVariable { + span, + name, + file_id, + } => { + let path = session.file_paths[file_id].display().to_string(); Report::build(ReportKind::Error, path.clone(), span.lo) - .with_code("UseOfUndeclaredVariable") - .with_label( - Label::new((path, span.into())) - .with_message(format!("use of undeclared variable {:?}", name)) - .with_color(colors.next()), - ) - .finish() - }, + .with_code("UseOfUndeclaredVariable") + .with_label( + Label::new((path, span.into())) + .with_message(format!("use of undeclared variable {:?}", name)) + .with_color(colors.next()), + ) + .finish() + } } } diff --git a/lib/edlang_codegen_llvm/src/codegen.rs b/lib/edlang_codegen_llvm/src/codegen.rs index 6699f428b..6bdd070db 100644 --- a/lib/edlang_codegen_llvm/src/codegen.rs +++ b/lib/edlang_codegen_llvm/src/codegen.rs @@ -35,17 +35,19 @@ struct ModuleCompileCtx<'ctx, 'm> { di_builder: DebugInfoBuilder<'ctx>, di_unit: DICompileUnit<'ctx>, target_data: TargetData, - _module_id: DefId, + module_id: DefId, di_namespace: DIScope<'ctx>, } impl<'ctx, 'm> ModuleCompileCtx<'ctx, 'm> { - pub fn _get_module_body(&self) -> &ModuleBody { - self.ctx.program.modules.get(&self._module_id).unwrap() + pub fn get_module_body(&self) -> &ModuleBody { + self.ctx.program.modules.get(&self.module_id).unwrap() } pub fn set_debug_loc(&self, scope: DIScope<'ctx>, span: Span) -> DILocation<'ctx> { - let (_, line, column) = self.ctx.session.source.get_offset_line(span.lo).unwrap(); + let (_, line, column) = self.ctx.session.sources[self.get_module_body().file_id] + .get_offset_line(span.lo) + .unwrap(); let debug_loc = self.di_builder.create_debug_location( self.ctx.context, line as u32 + 1, @@ -98,25 +100,39 @@ pub fn compile(session: &Session, program: &ProgramBody) -> Result inkwell::debug_info::DWARFEmissionKind::None, + edlang_session::DebugInfo::Full => inkwell::debug_info::DWARFEmissionKind::Full, + }, module.module_id.program_id.try_into().unwrap(), // compile unit id? false, false, @@ -135,7 +151,7 @@ pub fn compile(session: &Session, program: &ProgramBody) -> Result, fn_id: DefId) { fn_value.set_call_conventions(0); // cconv - let (_, line, _col) = ctx - .ctx - .session - .source + let (_, line, _col) = ctx.ctx.session.sources[ctx.get_module_body().file_id] .get_offset_line(body.fn_span.lo) .unwrap(); @@ -1480,10 +1493,7 @@ fn compile_debug_type<'ctx>(ctx: &ModuleCompileCtx<'ctx, '_>, ty: &ir::TypeInfo) fields.push(ty); } - let (_, line, _column) = ctx - .ctx - .session - .source + let (_, line, _column) = ctx.ctx.session.sources[ctx.get_module_body().file_id] .get_offset_line(body.span.lo) .unwrap(); let real_ty = compile_basic_type(ctx, ty); diff --git a/lib/edlang_codegen_llvm/src/lib.rs b/lib/edlang_codegen_llvm/src/lib.rs index 6824afdae..bff8e722b 100644 --- a/lib/edlang_codegen_llvm/src/lib.rs +++ b/lib/edlang_codegen_llvm/src/lib.rs @@ -7,7 +7,6 @@ use edlang_session::Session; use ir::ProgramBody; pub mod codegen; -pub mod linker; pub fn compile( session: &Session, diff --git a/lib/edlang_driver/Cargo.toml b/lib/edlang_driver/Cargo.toml index 947ed9001..db4662cf5 100644 --- a/lib/edlang_driver/Cargo.toml +++ b/lib/edlang_driver/Cargo.toml @@ -25,6 +25,7 @@ edlang_parser = { version = "0.0.1-alpha.12", path = "../edlang_parser" } edlang_session = { version = "0.0.1-alpha.12", path = "../edlang_session" } tracing = { workspace = true } tracing-subscriber = { version = "0.3.18", features = ["env-filter"] } +walkdir = "2.5.0" [dev-dependencies] tempfile = "3.10.1" diff --git a/lib/edlang_driver/src/lib.rs b/lib/edlang_driver/src/lib.rs index edbd9decc..a425373de 100644 --- a/lib/edlang_driver/src/lib.rs +++ b/lib/edlang_driver/src/lib.rs @@ -1,49 +1,60 @@ use std::{path::PathBuf, time::Instant}; -use anyhow::{bail, Result}; -use ariadne::Source; +use anyhow::Result; +use ariadne::{sources, Source}; use clap::Parser; -use edlang_codegen_llvm::linker::{link_binary, link_shared_lib}; use edlang_lowering::lower_modules; use edlang_session::{DebugInfo, OptLevel, Session}; +use walkdir::WalkDir; + +use crate::linker::{link_binary, link_shared_lib}; + +pub mod linker; #[derive(Parser, Debug)] #[command(author, version, about = "edlang compiler driver", long_about = None, bin_name = "edlangc")] pub struct CompilerArgs { /// The input file. - input: PathBuf, + pub input: PathBuf, + + /// The output file. + pub output: PathBuf, /// Build for release with all optimizations. #[arg(short, long, default_value_t = false)] - release: bool, + pub release: bool, /// Set the optimization level, 0,1,2,3 #[arg(short = 'O', long)] - optlevel: Option, + pub optlevel: Option, /// Always add debug info #[arg(long)] - debug_info: Option, + pub debug_info: Option, /// Build as a library. #[arg(short, long, default_value_t = false)] - library: bool, + pub library: bool, /// Print the edlang AST #[arg(long, default_value_t = false)] - ast: bool, + pub ast: bool, /// Print the edlang IR #[arg(long, default_value_t = false)] - ir: bool, + pub ir: bool, /// Output llvm ir #[arg(long, default_value_t = false)] - llvm: bool, + pub llvm: bool, /// Output asm #[arg(long, default_value_t = false)] - asm: bool, + pub asm: bool, + + /// Output a object file + #[arg(long, default_value_t = false)] + pub object: bool, } pub fn main() -> Result<()> { @@ -51,51 +62,59 @@ pub fn main() -> Result<()> { let args = CompilerArgs::parse(); - compile_single_file(args)?; + let object = compile(&args)?; + + if args.library { + link_shared_lib(&[object.clone()], &args.output)?; + } else { + link_binary(&[object.clone()], &args.output)?; + } + + if !args.object { + std::fs::remove_file(object)?; + } Ok(()) } -pub fn compile_single_file(args: CompilerArgs) -> Result<()> { - if !args.input.is_file() { - bail!("Input is not a file"); +pub fn compile(args: &CompilerArgs) -> Result { + let mut files = Vec::new(); + for entry in WalkDir::new(&args.input) { + let entry = entry?; + if let Some(ext) = entry.path().extension() { + if ext.eq_ignore_ascii_case("ed") { + files.push(entry.path().to_path_buf()); + } + } + } + + if files.is_empty() { + panic!("files is empty"); } let start_time = Instant::now(); - tracing_subscriber::fmt::init(); + let mut modules = Vec::new(); - let path = args.input.display().to_string(); - let source = std::fs::read_to_string(&args.input)?; + for path in files { + let source = std::fs::read_to_string(&path)?; - let modules = edlang_parser::parse_ast(&source); + let modules_ast = edlang_parser::parse_ast(&source); - let modules = match modules { - Ok(modules) => modules, - Err(error) => { - let report = edlang_parser::error_to_report(&path, &error)?; - edlang_parser::print_report(&path, &source, report)?; - std::process::exit(1) - } - }; - - let cwd = std::env::current_dir()?; - // todo: find a better name, "target" would clash with rust if running in the source tree. - let target_dir = cwd.join("target_ed/"); - if !target_dir.exists() { - std::fs::create_dir_all(&target_dir)?; + let modules_temp = match modules_ast { + Ok(modules) => modules, + 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, modules_temp)); } - let output_file = target_dir.join(PathBuf::from(args.input.file_name().unwrap())); - let output_file = if args.library { - output_file.with_extension(Session::get_platform_library_ext()) - } else if cfg!(target_os = "windows") { - output_file.with_extension("exe") - } else { - output_file.with_extension("") - }; let session = Session { - file_path: args.input, + file_paths: modules.iter().map(|x| x.0.clone()).collect(), debug_info: if let Some(debug_info) = args.debug_info { if debug_info { DebugInfo::Full @@ -119,50 +138,50 @@ pub fn compile_single_file(args: CompilerArgs) -> Result<()> { } else { OptLevel::None }, - source: Source::from(source), + sources: modules.iter().map(|x| Source::from(x.1.clone())).collect(), library: args.library, - target_dir, - output_file, + output_file: args.output.with_extension("o"), output_asm: args.asm, output_llvm: args.llvm, }; - tracing::debug!("Input file: {:#?}", session.file_path); - tracing::debug!("Target dir: {:#?}", session.target_dir); tracing::debug!("Output file: {:#?}", session.output_file); tracing::debug!("Is library: {:#?}", session.library); tracing::debug!("Optlevel: {:#?}", session.optlevel); tracing::debug!("Debug Info: {:#?}", session.debug_info); + let path_cache: Vec<_> = modules + .iter() + .map(|x| (x.0.display().to_string(), x.1.clone())) + .collect(); + let modules: Vec<_> = modules.iter().map(|x| x.2.clone()).collect(); + if args.ast { - println!("{:#?}", modules); - return Ok(()); + std::fs::write( + session.output_file.with_extension("ast"), + format!("{:#?}", modules), + )?; } let program_ir = match lower_modules(&modules) { Ok(ir) => ir, Err(error) => { let report = edlang_check::lowering_error_to_report(error, &session); - let path = session.file_path.display().to_string(); - report.eprint((path, session.source.clone()))?; + report.eprint(sources(path_cache))?; std::process::exit(1); } }; if args.ir { - println!("{:#?}", program_ir); - return Ok(()); + std::fs::write( + session.output_file.with_extension("ir"), + format!("{:#?}", program_ir), + )?; } let object_path = edlang_codegen_llvm::compile(&session, &program_ir).unwrap(); - if session.library { - link_shared_lib(&object_path, &session.output_file)?; - } else { - link_binary(&object_path, &session.output_file)?; - } - let elapsed = start_time.elapsed(); tracing::debug!("Done in {:?}", elapsed); - Ok(()) + Ok(object_path) } diff --git a/lib/edlang_codegen_llvm/src/linker.rs b/lib/edlang_driver/src/linker.rs similarity index 61% rename from lib/edlang_codegen_llvm/src/linker.rs rename to lib/edlang_driver/src/linker.rs index e4d363121..8ecb6252d 100644 --- a/lib/edlang_codegen_llvm/src/linker.rs +++ b/lib/edlang_driver/src/linker.rs @@ -1,39 +1,41 @@ -use std::path::Path; +use std::path::{Path, PathBuf}; use tracing::instrument; #[instrument(level = "debug")] -pub fn link_shared_lib(input_path: &Path, output_filename: &Path) -> std::io::Result<()> { - let args: &[&str] = { +pub fn link_shared_lib(objects: &[PathBuf], output_filename: &Path) -> std::io::Result<()> { + let objects: Vec<_> = objects.iter().map(|x| x.display().to_string()).collect(); + let output_filename = output_filename.to_string_lossy().to_string(); + + let args: Vec<_> = { #[cfg(target_os = "macos")] { - &[ + let mut args = vec![ "-demangle", "-no_deduplicate", "-dynamic", "-dylib", "-L/usr/local/lib", "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib", - &input_path.display().to_string(), - "-o", - &output_filename.display().to_string(), - "-lSystem", - ] + ]; + + args.extend(objects.iter().map(|x| x.as_str())); + + args.extend(&["-o", &output_filename, "-lSystem"]); + + args } #[cfg(target_os = "linux")] { - &[ - "--hash-style=gnu", - "--eh-frame-hdr", - "-shared", - "-o", - &output_filename.display().to_string(), - "-L/lib/../lib64", - "-L/usr/lib/../lib64", - "-lc", - "-O1", - &input_path.display().to_string(), - ] + let mut args = vec!["--hash-style=gnu", "--eh-frame-hdr", "-shared"]; + + args.extend(&["-o", &output_filename]); + + args.extend(&["-L/lib/../lib64", "-L/usr/lib/../lib64", "-lc", "-O1"]); + + args.extend(objects.iter().map(|x| x.as_str())); + + args } #[cfg(target_os = "windows")] { @@ -48,18 +50,24 @@ pub fn link_shared_lib(input_path: &Path, output_filename: &Path) -> std::io::Re } #[instrument(level = "debug")] -pub fn link_binary(input_path: &Path, output_filename: &Path) -> std::io::Result<()> { - let args: &[&str] = { +pub fn link_binary(objects: &[PathBuf], output_filename: &Path) -> std::io::Result<()> { + let objects: Vec<_> = objects.iter().map(|x| x.display().to_string()).collect(); + let output_filename = output_filename.to_string_lossy().to_string(); + + let args: Vec<_> = { #[cfg(target_os = "macos")] { - &[ + let mut args = vec![ "-L/usr/local/lib", "-L/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/lib", &input_path.display().to_string(), - "-o", - &output_filename.display().to_string(), - "-lSystem", - ] + ]; + + args.extend(objects.iter().map(|x| x.as_str())); + + args.extend(&["-o", &output_filename, "-lSystem"]); + + args } #[cfg(target_os = "linux")] { @@ -79,7 +87,7 @@ pub fn link_binary(input_path: &Path, output_filename: &Path) -> std::io::Result } }; - &[ + let mut args = vec![ "-pie", "--hash-style=gnu", "--eh-frame-hdr", @@ -89,8 +97,11 @@ pub fn link_binary(input_path: &Path, output_filename: &Path) -> std::io::Result "elf_x86_64", scrt1, crti, - "-o", - &output_filename.display().to_string(), + ]; + + args.extend(&["-o", &output_filename]); + + args.extend(&[ "-L/lib64", "-L/usr/lib64", "-L/lib/x86_64-linux-gnu", @@ -99,8 +110,11 @@ pub fn link_binary(input_path: &Path, output_filename: &Path) -> std::io::Result "-lc", "-O1", crtn, - &input_path.display().to_string(), - ] + ]); + + args.extend(objects.iter().map(|x| x.as_str())); + + args } #[cfg(target_os = "windows")] { diff --git a/lib/edlang_driver/tests/common.rs b/lib/edlang_driver/tests/common.rs index 90df33724..f83d94cb4 100644 --- a/lib/edlang_driver/tests/common.rs +++ b/lib/edlang_driver/tests/common.rs @@ -6,7 +6,7 @@ use std::{ }; use ariadne::Source; -use edlang_codegen_llvm::linker::{link_binary, link_shared_lib}; +use edlang_driver::linker::{link_binary, link_shared_lib}; use edlang_lowering::lower_modules; use edlang_session::{DebugInfo, OptLevel, Session}; use tempfile::TempDir; @@ -36,13 +36,9 @@ pub fn compile_program( ) -> Result> { let modules = edlang_parser::parse_ast(source).unwrap(); - let test_dir = tempfile::tempdir()?; - let test_dir_path = test_dir.path(); - let target_dir = test_dir_path.join("build_artifacts/"); - if !target_dir.exists() { - std::fs::create_dir_all(&target_dir)?; - } - let output_file = target_dir.join(PathBuf::from(name)); + let test_dir = tempfile::tempdir().unwrap(); + let test_dir_path = test_dir.path().canonicalize()?; + let output_file = test_dir_path.join(PathBuf::from(name)); let output_file = if library { output_file.with_extension(Session::get_platform_library_ext()) } else if cfg!(target_os = "windows") { @@ -51,26 +47,28 @@ pub fn compile_program( output_file.with_extension("") }; + let input_file = output_file.with_extension("ed"); + std::fs::write(&input_file, source)?; + let session = Session { - file_path: PathBuf::from(name), + file_paths: vec![input_file], debug_info: DebugInfo::Full, optlevel: OptLevel::Default, - source: Source::from(source.to_string()), + sources: vec![Source::from(source.to_string())], library, - target_dir, output_file, output_llvm: false, output_asm: false, }; - let program_ir = lower_modules(&modules)?; + let program_ir = lower_modules(&[modules]).unwrap(); - let object_path = edlang_codegen_llvm::compile(&session, &program_ir)?; + let object_path = edlang_codegen_llvm::compile(&session, &program_ir).unwrap(); if library { - link_shared_lib(&object_path, &session.output_file)?; + link_shared_lib(&[object_path.clone()], &session.output_file).unwrap(); } else { - link_binary(&object_path, &session.output_file)?; + link_binary(&[object_path.clone()], &session.output_file).unwrap(); } Ok(CompileResult { @@ -81,5 +79,5 @@ pub fn compile_program( } pub fn run_program(program: &Path, args: &[&str]) -> Result { - std::process::Command::new(dbg!(program)).args(args).spawn() + std::process::Command::new(program).args(args).spawn() } diff --git a/lib/edlang_ir/src/lib.rs b/lib/edlang_ir/src/lib.rs index 78d56bd22..1a92c3fc7 100644 --- a/lib/edlang_ir/src/lib.rs +++ b/lib/edlang_ir/src/lib.rs @@ -39,6 +39,7 @@ pub struct ProgramBody { pub struct ModuleBody { pub module_id: DefId, pub parent_ids: Vec, + pub file_id: usize, pub name: String, pub symbols: SymbolTable, /// Functions defined in this module. diff --git a/lib/edlang_lowering/src/common.rs b/lib/edlang_lowering/src/common.rs index d962559b6..fddd0e603 100644 --- a/lib/edlang_lowering/src/common.rs +++ b/lib/edlang_lowering/src/common.rs @@ -60,6 +60,7 @@ pub struct BodyBuilder { pub statements: Vec, pub name_to_local: HashMap, pub ret_local: usize, + pub file_id: usize, pub ctx: BuildCtx, } diff --git a/lib/edlang_lowering/src/errors.rs b/lib/edlang_lowering/src/errors.rs index b23a551c8..d0498cf1c 100644 --- a/lib/edlang_lowering/src/errors.rs +++ b/lib/edlang_lowering/src/errors.rs @@ -7,33 +7,60 @@ use crate::DefId; #[derive(Debug, Error, Clone)] pub enum LoweringError { #[error("module {module:?} not found")] - ModuleNotFound { span: Span, module: String }, + ModuleNotFound { + span: Span, + module: String, + file_id: usize, + }, #[error("function {function:?} not found")] - FunctionNotFound { span: Span, function: String }, + FunctionNotFound { + span: Span, + function: String, + file_id: usize, + }, #[error("symbol {:?} not found", symbol.name)] ImportNotFound { module_span: Span, import_span: Span, symbol: Ident, + file_id: usize, }, #[error("trying to mutate a non-mutable reference")] BorrowNotMutable { span: Span, type_span: Option, name: String, + file_id: usize, }, #[error("unrecognized type {name}")] - UnrecognizedType { span: Span, name: String }, + UnrecognizedType { + span: Span, + name: String, + file_id: usize, + }, #[error("id not found")] - IdNotFound { span: Span, id: DefId }, + IdNotFound { + span: Span, + id: DefId, + file_id: usize, + }, #[error("feature not yet implemented: {message}")] - NotYetImplemented { span: Span, message: &'static str }, + NotYetImplemented { + span: Span, + message: &'static str, + file_id: usize, + }, #[error("unexpected type")] UnexpectedType { span: Span, found: TypeKind, expected: TypeInfo, + file_id: usize, }, #[error("use of underclared variable {name:?}")] - UseOfUndeclaredVariable { span: Span, name: String }, + UseOfUndeclaredVariable { + span: Span, + name: String, + file_id: usize, + }, } diff --git a/lib/edlang_lowering/src/lib.rs b/lib/edlang_lowering/src/lib.rs index d34308021..115cef43d 100644 --- a/lib/edlang_lowering/src/lib.rs +++ b/lib/edlang_lowering/src/lib.rs @@ -15,27 +15,33 @@ mod common; pub mod errors; mod prepass; -pub fn lower_modules(modules: &[ast::Module]) -> Result { +pub fn lower_modules(modules: &[Vec]) -> Result { let mut ctx = BuildCtx::default(); // resolve symbols - for module in modules { - ctx = prepass::prepass_module(ctx, module)?; + for (file_id, modules) in modules.iter().enumerate() { + for module in modules { + ctx = prepass::prepass_module(ctx, module, file_id)?; + } } // resolve imports - for module in modules { - ctx = prepass::prepass_imports(ctx, module)?; + for (file_id, modules) in modules.iter().enumerate() { + for module in modules { + ctx = prepass::prepass_imports(ctx, module, file_id)?; + } } - for mod_def in modules { - let id = *ctx - .body - .top_level_module_names - .get(&mod_def.name.name) - .expect("module should exist"); + for modules in modules { + for mod_def in modules { + let id = *ctx + .body + .top_level_module_names + .get(&mod_def.name.name) + .expect("module should exist"); - ctx = lower_module(ctx, mod_def, id)?; + ctx = lower_module(ctx, mod_def, id)?; + } } Ok(ctx.body) @@ -157,6 +163,10 @@ fn lower_function( ret_local: 0, name_to_local: HashMap::new(), statements: Vec::new(), + file_id: { + let body = ctx.body.modules.get(&module_id).unwrap(); + body.file_id + }, ctx, }; @@ -450,6 +460,7 @@ fn lower_let(builder: &mut BodyBuilder, info: &ast::LetStmt) -> Result<(), Lower span: info.span, found: found_ty, expected: ty.clone(), + file_id: builder.file_id, }); } @@ -781,6 +792,7 @@ fn lower_binary_expr( span: Some(lhs_span), kind: lhs_ty, }, + file_id: builder.file_id, }); } @@ -1112,6 +1124,7 @@ fn lower_return( span, found: ty, expected: return_type.clone(), + file_id: builder.file_id, }); } @@ -1145,6 +1158,7 @@ fn lower_path( LoweringError::UseOfUndeclaredVariable { span: info.span, name: info.first.name.clone(), + file_id: builder.file_id, }, )?; @@ -1256,6 +1270,7 @@ pub fn lower_type( Err(LoweringError::UnrecognizedType { span: t.name.span, name: t.name.name.clone(), + file_id: module.file_id, })? } } diff --git a/lib/edlang_lowering/src/prepass.rs b/lib/edlang_lowering/src/prepass.rs index d5ec4f7c3..4b8b347ef 100644 --- a/lib/edlang_lowering/src/prepass.rs +++ b/lib/edlang_lowering/src/prepass.rs @@ -6,7 +6,11 @@ use super::common::BuildCtx; use edlang_ast as ast; use edlang_ir::ModuleBody; -pub fn prepass_module(mut ctx: BuildCtx, mod_def: &ast::Module) -> Result { +pub fn prepass_module( + mut ctx: BuildCtx, + mod_def: &ast::Module, + file_id: usize, +) -> Result { let module_id = ctx.gen.next_defid(); tracing::debug!("running ir prepass on module {:?}", module_id); @@ -29,6 +33,7 @@ pub fn prepass_module(mut ctx: BuildCtx, mod_def: &ast::Module) -> Result Result Result { tracing::debug!("running ir prepass on submodule {:?}", module_id); let mut submodule_parents_ids = parent_ids.to_vec(); @@ -137,6 +143,7 @@ pub fn prepass_sub_module( types: Default::default(), constants: Default::default(), span: mod_def.span, + file_id, }; for ct in &mod_def.contents { @@ -201,7 +208,7 @@ pub fn prepass_sub_module( for ct in &mod_def.contents { if let ast::ModuleStatement::Module(info) = ct { let next_id = ctx.gen.next_defid(); - ctx = prepass_sub_module(ctx, &submodule_parents_ids, next_id, info)?; + ctx = prepass_sub_module(ctx, &submodule_parents_ids, next_id, info, file_id)?; } } @@ -211,6 +218,7 @@ pub fn prepass_sub_module( pub fn prepass_imports( mut ctx: BuildCtx, mod_def: &ast::Module, + file_id: usize, ) -> Result { let mod_id = *ctx .body @@ -226,6 +234,7 @@ pub fn prepass_imports( .ok_or_else(|| LoweringError::ModuleNotFound { span: import.module[0].span, module: import.module[0].name.clone(), + file_id, })?; let mut imported_module = ctx.body @@ -234,6 +243,7 @@ pub fn prepass_imports( .ok_or(LoweringError::IdNotFound { span: mod_def.span, id: *imported_module_id, + file_id, })?; for module_path in import.module.iter().skip(1) { @@ -244,11 +254,13 @@ pub fn prepass_imports( .ok_or_else(|| LoweringError::ModuleNotFound { span: module_path.span, module: module_path.name.clone(), + file_id, })?; imported_module = ctx.body.modules.get(imported_module_id).ok_or({ LoweringError::IdNotFound { span: module_path.span, id: *imported_module_id, + file_id, } })?; } @@ -269,6 +281,7 @@ pub fn prepass_imports( module_span: mod_def.span, import_span: import.span, symbol: sym.clone(), + file_id, })?; } } @@ -283,7 +296,7 @@ pub fn prepass_imports( for c in &mod_def.contents { if let ast::ModuleStatement::Module(info) = c { - ctx = prepass_imports_submodule(ctx, info, mod_id)?; + ctx = prepass_imports_submodule(ctx, info, mod_id, file_id)?; } } @@ -294,6 +307,7 @@ pub fn prepass_imports_submodule( mut ctx: BuildCtx, mod_def: &ast::Module, parent_id: DefId, + file_id: usize, ) -> Result { let mod_id = *ctx .body @@ -306,6 +320,7 @@ pub fn prepass_imports_submodule( .ok_or_else(|| LoweringError::ModuleNotFound { span: mod_def.span, module: mod_def.name.name.clone(), + file_id, })?; for import in &mod_def.imports { @@ -316,11 +331,13 @@ pub fn prepass_imports_submodule( .ok_or_else(|| LoweringError::ModuleNotFound { span: import.module[0].span, module: import.module[0].name.clone(), + file_id, })?; let mut imported_module = ctx.body.modules.get(imported_module_id).ok_or_else(|| { LoweringError::ModuleNotFound { span: import.module[0].span, module: import.module[0].name.clone(), + file_id, } })?; @@ -332,11 +349,13 @@ pub fn prepass_imports_submodule( .ok_or_else(|| LoweringError::ModuleNotFound { span: module_path.span, module: module_path.name.clone(), + file_id, })?; imported_module = ctx.body.modules.get(imported_module_id).ok_or({ LoweringError::IdNotFound { span: import.span, id: *imported_module_id, + file_id, } })?; } @@ -357,6 +376,7 @@ pub fn prepass_imports_submodule( module_span: mod_def.span, import_span: import.span, symbol: sym.clone(), + file_id, })?; } } diff --git a/lib/edlang_session/src/lib.rs b/lib/edlang_session/src/lib.rs index eda263dac..1355b7ec6 100644 --- a/lib/edlang_session/src/lib.rs +++ b/lib/edlang_session/src/lib.rs @@ -4,13 +4,11 @@ use ariadne::Source; #[derive(Debug, Clone)] pub struct Session { - pub file_path: PathBuf, + pub file_paths: Vec, pub debug_info: DebugInfo, pub optlevel: OptLevel, - pub source: Source, + pub sources: Vec>, pub library: bool, - /// The directory where to store artifacts and intermediate files such as object files. - pub target_dir: PathBuf, pub output_file: PathBuf, pub output_llvm: bool, pub output_asm: bool,