feat: Module file declarations, fixes

This commit is contained in:
Edgar 2024-03-13 12:06:33 +01:00
parent 7c3bc054e5
commit f150dd6c61
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
8 changed files with 127 additions and 81 deletions

View file

@ -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 lib_ed = src_dir.join("lib.ed");
let main_ed = src_dir.join("main.ed");
let start = Instant::now();
for file in [main_ed, lib_ed] {
if file.exists() {
let is_lib = file.file_stem().unwrap() == "lib";
let compile_args = CompilerArgs {
input: src_dir,
output: output.clone(),
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: !has_main,
library: is_lib,
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)?;
if compile_args.library {
link_shared_lib(&[object], &compile_args.output)?;
} else {
link_binary(&[object], &output)?;
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"
}
}

View file

@ -6,6 +6,7 @@ pub use edlang_span::Span;
pub struct Module {
pub name: Ident,
pub imports: Vec<Import>,
pub external_modules: Vec<Ident>,
pub contents: Vec<ModuleStatement>,
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<FnParam>,
pub return_type: Option<Type>,
pub body: Option<Block>,

View file

@ -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,6 +272,8 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit
DIFlagsConstants::PRIVATE
},
);
if fn_value.get_subprogram().is_none() {
let subprogram = ctx.di_builder.create_function(
ctx.di_namespace,
&body.name,
@ -279,14 +281,15 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit
ctx.di_unit.get_file(),
line as u32 + 1,
di_type,
!body.is_pub,
is_definition,
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> {
let body = ctx.ctx.program.functions.get(&fn_id).unwrap();

View file

@ -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,26 +77,11 @@ pub fn main() -> Result<()> {
Ok(())
}
pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
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");
}
if files.is_empty() {
panic!("files is empty");
}
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(
@ -113,9 +98,28 @@ pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
std::process::exit(1)
}
};
modules.push((path, source, module_temp));
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)?;
}
modules.push((path, source, module_temp));
Ok(())
}
pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
let start_time = Instant::now();
let mut modules = Vec::new();
parse_file(&mut modules, args.input.clone())?;
let session = Session {
file_paths: modules.iter().map(|x| x.0.clone()).collect(),
debug_info: if let Some(debug_info) = args.debug_info {
@ -181,7 +185,8 @@ pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
)?;
}
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);

View file

@ -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,20 +96,16 @@ 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
)
}
}
}
#[derive(Debug, Clone)]
pub struct AdtBody {

View file

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

View file

@ -28,6 +28,7 @@ extern {
"in" => Token::KeywordIn,
"extern" => Token::KeywordExtern,
"as" => Token::KeywordAs,
"exported" => Token::KeywordExported,
// literals
"identifier" => Token::Identifier(<String>),
@ -426,16 +427,18 @@ pub(crate) Function: ast::Function = {
<return_type:("->" <Type>)?> ";" <hi:@R> => 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),
},
<lo:@L> <is_public:"pub"?> "fn" <name:Ident> "(" <params:Comma<FnParam>> ")"
<lo:@L> <is_public:"pub"?> <is_exported:"exported"?> "fn" <name:Ident> "(" <params:Comma<FnParam>> ")"
<return_type:("->" <Type>)?> <body:Block> <hi:@R> => 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:Ident> ";" => name
}
pub TopLevelModule: ast::Module = {
<lo:@L> <imports:List<Import>?> <contents:List<ModuleStatement>> <hi:@R> => ast::Module {
<lo:@L> <imports:List<Import>?> <external_modules:List<ExternalModule>?> <contents:List<ModuleStatement>> <hi:@R> => 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 = {
<lo:@L> "mod" <name:Ident> "{" <imports:List<Import>?> <contents:List<ModuleStatement>> "}" <hi:@R> => ast::Module {
name,
imports: imports.unwrap_or(vec![]),
external_modules: Vec::new(),
contents,
span: ast::Span::new(lo, hi),
}

View file

@ -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())]