feat: usable edb

This commit is contained in:
Edgar 2024-03-11 12:02:14 +01:00
parent 22498719e1
commit bd3c4da7ea
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
16 changed files with 528 additions and 230 deletions

8
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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<String>,
},
}
@ -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(())

View file

@ -9,11 +9,15 @@ pub fn lowering_error_to_report(
error: LoweringError,
session: &Session,
) -> Report<(String, Range<usize>)> {
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()
}
}
}

View file

@ -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<PathBuf, Box<
info!("compiling for: {:?}", target.get_description());
let filename = session.file_path.file_name().unwrap().to_string_lossy();
let dir = session.file_path.parent().unwrap().to_string_lossy();
for module_id in program.top_level_modules.iter() {
let module = ctx.program.modules.get(module_id).unwrap();
let file_path = session.file_paths[module.file_id].clone();
let abs_file_path = file_path
.canonicalize()
.expect("failed to canonicalize file path");
let filename = file_path.file_name().unwrap().to_str().unwrap();
let dirname = abs_file_path
.parent()
.unwrap()
.file_name()
.unwrap()
.to_str()
.unwrap();
let llvm_module = context.create_module(&module.name);
llvm_module.set_source_file_name(&filename);
llvm_module.set_source_file_name(filename);
llvm_module.set_triple(&triple);
llvm_module.set_data_layout(&machine.get_target_data().get_data_layout());
let (di_builder, di_unit) = llvm_module.create_debug_info_builder(
true,
inkwell::debug_info::DWARFSourceLanguage::Rust,
&filename,
&dir,
filename,
dirname,
"edlang",
true,
"", // compiler flags
1,
"", // split name
inkwell::debug_info::DWARFEmissionKind::Full,
match session.debug_info {
edlang_session::DebugInfo::None => 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<PathBuf, Box<
di_unit,
builder: &builder,
target_data: machine.get_target_data(),
_module_id: *module_id,
module_id: *module_id,
di_namespace,
};
@ -234,10 +250,7 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, 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);

View file

@ -7,7 +7,6 @@ use edlang_session::Session;
use ir::ProgramBody;
pub mod codegen;
pub mod linker;
pub fn compile(
session: &Session,

View file

@ -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"

View file

@ -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<u8>,
pub optlevel: Option<u8>,
/// Always add debug info
#[arg(long)]
debug_info: Option<bool>,
pub debug_info: Option<bool>,
/// 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<PathBuf> {
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)
}

View file

@ -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")]
{

View file

@ -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<CompileResult, Box<dyn std::error::Error>> {
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<Child, std::io::Error> {
std::process::Command::new(dbg!(program)).args(args).spawn()
std::process::Command::new(program).args(args).spawn()
}

View file

@ -39,6 +39,7 @@ pub struct ProgramBody {
pub struct ModuleBody {
pub module_id: DefId,
pub parent_ids: Vec<DefId>,
pub file_id: usize,
pub name: String,
pub symbols: SymbolTable,
/// Functions defined in this module.

View file

@ -60,6 +60,7 @@ pub struct BodyBuilder {
pub statements: Vec<Statement>,
pub name_to_local: HashMap<String, usize>,
pub ret_local: usize,
pub file_id: usize,
pub ctx: BuildCtx,
}

View file

@ -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<Span>,
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,
},
}

View file

@ -15,27 +15,33 @@ mod common;
pub mod errors;
mod prepass;
pub fn lower_modules(modules: &[ast::Module]) -> Result<ProgramBody, LoweringError> {
pub fn lower_modules(modules: &[Vec<ast::Module>]) -> Result<ProgramBody, LoweringError> {
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,
})?
}
}

View file

@ -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<BuildCtx, LoweringError> {
pub fn prepass_module(
mut ctx: BuildCtx,
mod_def: &ast::Module,
file_id: usize,
) -> Result<BuildCtx, LoweringError> {
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<BuildC
constants: Default::default(),
imports: Default::default(),
span: mod_def.span,
file_id,
},
);
@ -106,7 +111,7 @@ pub fn prepass_module(mut ctx: BuildCtx, mod_def: &ast::Module) -> Result<BuildC
.expect("module should exist");
let next_id = *current_module.symbols.modules.get(&info.name.name).unwrap();
ctx = prepass_sub_module(ctx, &[module_id], next_id, info)?;
ctx = prepass_sub_module(ctx, &[module_id], next_id, info, file_id)?;
}
}
@ -118,6 +123,7 @@ pub fn prepass_sub_module(
parent_ids: &[DefId],
module_id: DefId,
mod_def: &ast::Module,
file_id: usize,
) -> Result<BuildCtx, LoweringError> {
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<BuildCtx, LoweringError> {
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<BuildCtx, LoweringError> {
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,
})?;
}
}

View file

@ -4,13 +4,11 @@ use ariadne::Source;
#[derive(Debug, Clone)]
pub struct Session {
pub file_path: PathBuf,
pub file_paths: Vec<PathBuf>,
pub debug_info: DebugInfo,
pub optlevel: OptLevel,
pub source: Source<String>,
pub sources: Vec<Source<String>>,
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,