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 { if bin {
std::fs::write( std::fs::write(
path.join("src").join("main.ed"), path.join("src").join("main.ed"),
r#"pub fn main() -> i32 {{ r#"pub fn main() -> i32 {
return 0; return 0;
}"#, }"#,
)?; )?;
@ -123,7 +123,7 @@ fn main() -> Result<()> {
if lib { if lib {
std::fs::write( std::fs::write(
path.join("src").join("lib.ed"), path.join("src").join("lib.ed"),
r#"pub fn main() -> i32 {{ r#"pub fn main() -> i32 {
return 0; return 0;
}"#, }"#,
)?; )?;
@ -201,7 +201,6 @@ fn main() -> Result<()> {
std::fs::create_dir_all(&target_dir)?; 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 output = target_dir.join(config.package.name);
let (profile, profile_name) = if let Some(profile) = profile { 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 { let compile_args = CompilerArgs {
input: src_dir, input: file,
output: output.clone(), 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, release,
optlevel: Some(profile.opt_level), optlevel: Some(profile.opt_level),
debug_info: Some(profile.debug_info), debug_info: Some(profile.debug_info),
library: !has_main, library: is_lib,
ast: false, ast: false,
ir: false, ir: false,
llvm: true, llvm: true,
asm: false, asm: false,
object: true, object: true,
}; };
let start = Instant::now();
let object = compile(&compile_args)?; let object = compile(&compile_args)?;
if !has_main { if compile_args.library {
link_shared_lib(&[object], &output)?; link_shared_lib(&[object], &compile_args.output)?;
} else { } else {
link_binary(&[object], &output)?; link_binary(&[object], &compile_args.output)?;
}
}
} }
let elapsed = start.elapsed(); let elapsed = start.elapsed();
@ -280,3 +296,13 @@ fn main() -> Result<()> {
Ok(()) 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 struct Module {
pub name: Ident, pub name: Ident,
pub imports: Vec<Import>, pub imports: Vec<Import>,
pub external_modules: Vec<Ident>,
pub contents: Vec<ModuleStatement>, pub contents: Vec<ModuleStatement>,
pub span: Span, pub span: Span,
} }
@ -138,6 +139,7 @@ pub struct Function {
pub name: Ident, pub name: Ident,
pub is_extern: bool, pub is_extern: bool,
pub is_public: bool, pub is_public: bool,
pub is_exported: bool,
pub params: Vec<FnParam>, pub params: Vec<FnParam>,
pub return_type: Option<Type>, pub return_type: Option<Type>,
pub body: Option<Block>, 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( let fn_value = ctx.module.add_function(
&body.get_mangled_name(), &body.get_mangled_name(),
fn_type, fn_type,
Some(if body.is_pub || body.is_extern { Some(if body.is_extern || body.is_exported {
inkwell::module::Linkage::External inkwell::module::Linkage::External
} else { } else {
inkwell::module::Linkage::Private inkwell::module::Linkage::Private
@ -272,6 +272,8 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit
DIFlagsConstants::PRIVATE DIFlagsConstants::PRIVATE
}, },
); );
if fn_value.get_subprogram().is_none() {
let subprogram = ctx.di_builder.create_function( let subprogram = ctx.di_builder.create_function(
ctx.di_namespace, ctx.di_namespace,
&body.name, &body.name,
@ -279,14 +281,15 @@ fn compile_fn_signature(ctx: &ModuleCompileCtx<'_, '_>, fn_id: DefId, is_definit
ctx.di_unit.get_file(), ctx.di_unit.get_file(),
line as u32 + 1, line as u32 + 1,
di_type, di_type,
!body.is_pub, body.is_exported || body.is_extern,
is_definition, is_definition && !body.is_extern,
line as u32 + 1, line as u32 + 1,
0, 0,
false, false,
); );
fn_value.set_subprogram(subprogram); fn_value.set_subprogram(subprogram);
} }
}
fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> { fn compile_fn(ctx: &ModuleCompileCtx, fn_id: DefId) -> Result<(), BuilderError> {
let body = ctx.ctx.program.functions.get(&fn_id).unwrap(); 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 anyhow::Result;
use ariadne::{sources, Source}; use ariadne::{sources, Source};
use clap::Parser; use clap::Parser;
use edlang_ast::Module;
use edlang_lowering::lower_modules; use edlang_lowering::lower_modules;
use edlang_session::{DebugInfo, OptLevel, Session}; use edlang_session::{DebugInfo, OptLevel, Session};
use walkdir::WalkDir;
use crate::linker::{link_binary, link_shared_lib}; use crate::linker::{link_binary, link_shared_lib};
@ -77,26 +77,11 @@ pub fn main() -> Result<()> {
Ok(()) Ok(())
} }
pub fn compile(args: &CompilerArgs) -> Result<PathBuf> { pub fn parse_file(modules: &mut Vec<(PathBuf, String, Module)>, mut path: PathBuf) -> Result<()> {
let mut files = Vec::new(); if path.is_dir() {
for entry in WalkDir::new(&args.input).sort_by_file_name() { path = path.join("mod.ed");
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();
let mut modules = Vec::new();
for path in files {
let source = std::fs::read_to_string(&path)?; let source = std::fs::read_to_string(&path)?;
let module_ast = edlang_parser::parse_ast( let module_ast = edlang_parser::parse_ast(
@ -113,9 +98,28 @@ pub fn compile(args: &CompilerArgs) -> Result<PathBuf> {
std::process::exit(1) 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 { let session = Session {
file_paths: modules.iter().map(|x| x.0.clone()).collect(), file_paths: modules.iter().map(|x| x.0.clone()).collect(),
debug_info: if let Some(debug_info) = args.debug_info { 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(); let elapsed = start_time.elapsed();
tracing::debug!("Done in {:?}", elapsed); tracing::debug!("Done in {:?}", elapsed);

View file

@ -70,6 +70,8 @@ pub struct Body {
pub def_id: DefId, pub def_id: DefId,
pub is_pub: bool, pub is_pub: bool,
pub is_extern: bool, pub is_extern: bool,
// exported means externally available in a shared library or as main
pub is_exported: bool,
pub name: String, pub name: String,
pub locals: SmallVec<[Local; 4]>, pub locals: SmallVec<[Local; 4]>,
pub blocks: SmallVec<[BasicBlock; 8]>, pub blocks: SmallVec<[BasicBlock; 8]>,
@ -94,20 +96,16 @@ impl Body {
} }
pub fn get_mangled_name(&self) -> String { pub fn get_mangled_name(&self) -> String {
if self.is_extern { if self.is_extern || self.is_exported {
return self.name.clone(); return self.name.clone();
} }
if self.name == "main" {
"main".to_string()
} else {
format!( format!(
"{}@{}@{}", "{}@{}@{}",
self.name, self.def_id.program_id, self.def_id.id self.name, self.def_id.program_id, self.def_id.id
) )
} }
} }
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct AdtBody { pub struct AdtBody {

View file

@ -149,8 +149,9 @@ fn lower_function(
let body = ctx.body.modules.get(&module_id).unwrap(); let body = ctx.body.modules.get(&module_id).unwrap();
*body.symbols.functions.get(&func.name.name).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_extern: func.is_extern,
is_exported: func.is_exported || func.name.name == "main",
fn_span: func.span, fn_span: func.span,
}, },
local_module: module_id, local_module: module_id,

View file

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

View file

@ -57,6 +57,8 @@ pub enum Token {
KeywordExtern, KeywordExtern,
#[token("as")] #[token("as")]
KeywordAs, KeywordAs,
#[token("exported")]
KeywordExported,
// Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/ // Modern way of allowing identifiers, read: https://unicode.org/reports/tr31/
#[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())] #[regex(r"[\p{XID_Start}_]\p{XID_Continue}*", |lex| lex.slice().to_string())]