diff --git a/Cargo.lock b/Cargo.lock index 76dff105..baf1b6ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,7 @@ dependencies = [ "cstr", "derivative", "derive_more", + "enum_dispatch", "humansize", "ignore", "indexmap", @@ -201,6 +202,18 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "enum_dispatch" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd" +dependencies = [ + "once_cell", + "proc-macro2", + "quote", + "syn 2.0.74", +] + [[package]] name = "equivalent" version = "1.0.1" diff --git a/Cargo.toml b/Cargo.toml index 65c63b20..c575f50c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,4 +32,5 @@ humansize = "2.1.3" thousands = "0.2.0" path-absolutize = { version = "3.1.1", features = ["once_cell_cache"] } ordered-float = "4.5.0" +enum_dispatch = "0.3.13" diff --git a/src/cli.rs b/src/cli.rs deleted file mode 100644 index b4f6d75d..00000000 --- a/src/cli.rs +++ /dev/null @@ -1,151 +0,0 @@ -use crate::target::{Target, TargetOs}; -use std::{path::PathBuf, str::FromStr}; - -pub struct Command { - pub kind: CommandKind, -} - -impl Command { - pub fn parse_env_args() -> Result { - let mut args = std::env::args().skip(1).peekable(); - - match args.peek().map(|string| string.as_str()) { - None | Some("-h" | "--help") => { - show_help(); - Err(()) - } - Some("new") => Self::parse_new_project(args), - _ => Self::parse_build_project(args), - } - } - - fn parse_build_project(mut args: impl Iterator) -> Result { - let mut filename = None; - let mut options = BuildOptions::default(); - - while let Some(option) = args.next() { - match option.as_str() { - "-e" => options.excute_result = true, - "--emit-ir" => options.emit_ir = true, - "--emit-llvm-ir" => options.emit_llvm_ir = true, - "--interpret" => { - options.interpret = true; - options.coerce_main_signature = false; - } - "--windows" => { - options.target = Target::generic_os(TargetOs::Windows); - } - "--mac" | "--macos" => { - options.target = Target::generic_os(TargetOs::Mac); - } - "--linux" => { - options.target = Target::generic_os(TargetOs::Linux); - } - "--freebsd" => { - options.target = Target::generic_os(TargetOs::FreeBsd); - } - "--infrastructure" => { - let Some(infrastructure) = args.next() else { - eprintln!("error: Expected infrastructure path after '--infrastructure'"); - return Err(()); - }; - - options.infrastructure = Some( - PathBuf::from_str(&infrastructure) - .expect("invalid non-utf-8 infrastructure path"), - ); - } - _ => { - if filename.replace(option).is_some() { - eprintln!("error: Multiple paths specified"); - return Err(()); - } - } - } - } - - let Some(filename) = filename else { - eprintln!("error: No folder or filename specified"); - return Err(()); - }; - - Ok(Self { - kind: CommandKind::Build(BuildCommand { filename, options }), - }) - } - - fn parse_new_project(mut args: impl Iterator) -> Result { - // Skip over 'new' command keyword - args.next().unwrap(); - - let project_name = match args.next() { - Some(project_name) => project_name, - None => { - println!("adept new "); - return Err(()); - } - }; - - Ok(Self { - kind: CommandKind::New(NewCommand { project_name }), - }) - } -} - -#[derive(Clone, Debug)] -pub enum CommandKind { - Build(BuildCommand), - New(NewCommand), -} - -#[derive(Clone, Debug)] -pub struct BuildCommand { - pub filename: String, - pub options: BuildOptions, -} - -#[derive(Clone, Debug)] -pub struct BuildOptions { - pub emit_llvm_ir: bool, - pub emit_ir: bool, - pub interpret: bool, - pub coerce_main_signature: bool, - pub excute_result: bool, - pub allow_experimental_pragma_features: bool, - pub use_pic: Option, - pub target: Target, - pub infrastructure: Option, -} - -impl Default for BuildOptions { - fn default() -> Self { - let current_exe = std::env::current_exe() - .expect("failed to get adept executable location") - .parent() - .expect("parent folder") - .to_path_buf(); - - let infrastructure = current_exe.join("infrastructure"); - - Self { - emit_llvm_ir: false, - emit_ir: false, - interpret: false, - coerce_main_signature: true, - excute_result: false, - allow_experimental_pragma_features: false, - use_pic: None, - target: Target::HOST, - infrastructure: Some(infrastructure), - } - } -} - -#[derive(Clone, Debug)] -pub struct NewCommand { - pub project_name: String, -} - -fn show_help() { - println!("usage: adept FILENAME"); -} diff --git a/src/cli/build/invoke.rs b/src/cli/build/invoke.rs new file mode 100644 index 00000000..9faafba9 --- /dev/null +++ b/src/cli/build/invoke.rs @@ -0,0 +1,105 @@ +use super::BuildCommand; +use crate::{ + c, + cli::CliInvoke, + compiler::Compiler, + diagnostics::{DiagnosticFlags, Diagnostics, WarningDiagnostic}, + single_file_only::compile_single_file_only, + source_files::SourceFiles, + target::{Target, TargetArch, TargetOs}, + text::{IntoText, IntoTextStream}, + unerror::unerror, + workspace::compile_workspace, +}; +use std::{fs::metadata, path::Path}; + +impl CliInvoke for BuildCommand { + fn invoke(self) -> Result<(), ()> { + let BuildCommand { filename, options } = self; + let source_files = SourceFiles::new(); + let filepath = Path::new(&filename); + let diagnostics = Diagnostics::new(&source_files, DiagnosticFlags::default()); + let target = options.target; + + let Ok(metadata) = metadata(filepath) else { + eprintln!("error: File or folder does not exist"); + return Err(()); + }; + + ensure_supported_target(&target, &diagnostics); + + let mut compiler = Compiler { + options, + source_files: &source_files, + diagnostics: &diagnostics, + version: Default::default(), + link_filenames: Default::default(), + link_frameworks: Default::default(), + }; + + if metadata.is_dir() { + compile_workspace(&mut compiler, filepath, None) + } else if filepath.extension().unwrap_or_default() == "h" { + compile_header(&compiler, filepath) + } else { + compile_single_file_only(&mut compiler, filepath.parent().unwrap(), filepath) + } + } +} + +fn ensure_supported_target(target: &Target, diagnostics: &Diagnostics) { + if target.arch().is_none() { + diagnostics.push(WarningDiagnostic::plain( + "Target architecture is not supported, falling back to best guess", + )); + } + + if target.os().is_none() { + diagnostics.push(WarningDiagnostic::plain( + "Target os is not supported, falling back to best guess", + )); + } + + match target.os().zip(target.arch()) { + Some((TargetOs::Windows, TargetArch::X86_64)) => (), + Some((TargetOs::Windows, TargetArch::Aarch64)) => (), + Some((TargetOs::Mac, TargetArch::X86_64)) => (), + Some((TargetOs::Mac, TargetArch::Aarch64)) => (), + Some((TargetOs::Linux, TargetArch::X86_64)) => (), + Some((TargetOs::Linux, TargetArch::Aarch64)) => (), + Some((TargetOs::FreeBsd, TargetArch::X86_64)) => (), + None => (), + #[allow(unreachable_patterns)] + _ => { + diagnostics.push(WarningDiagnostic::plain( + "Host os/architecture configuration is not officially supported, taking best guess", + )); + } + } +} + +fn compile_header(compiler: &Compiler, filepath: &Path) -> Result<(), ()> { + let source_files = compiler.source_files; + + let content = std::fs::read_to_string(filepath).map_err(|err| { + eprintln!("{}", err); + () + })?; + + let header_key = source_files.add(filepath.into(), content); + + let header_contents = source_files + .get(header_key) + .content() + .chars() + .into_text_stream(header_key) + .into_text(); + + let preprocessed = unerror( + c::preprocessor::preprocess(header_contents, &compiler.diagnostics), + &source_files, + )?; + + println!("{preprocessed:?}"); + return Ok(()); +} diff --git a/src/cli/build/mod.rs b/src/cli/build/mod.rs new file mode 100644 index 00000000..bfe4d84d --- /dev/null +++ b/src/cli/build/mod.rs @@ -0,0 +1,11 @@ +mod invoke; +mod options; +mod parse; + +pub use options::BuildOptions; + +#[derive(Clone, Debug)] +pub struct BuildCommand { + pub filename: String, + pub options: BuildOptions, +} diff --git a/src/cli/build/options.rs b/src/cli/build/options.rs new file mode 100644 index 00000000..02f22538 --- /dev/null +++ b/src/cli/build/options.rs @@ -0,0 +1,39 @@ +use crate::target::Target; +use std::path::PathBuf; + +#[derive(Clone, Debug)] +pub struct BuildOptions { + pub emit_llvm_ir: bool, + pub emit_ir: bool, + pub interpret: bool, + pub coerce_main_signature: bool, + pub excute_result: bool, + pub allow_experimental_pragma_features: bool, + pub use_pic: Option, + pub target: Target, + pub infrastructure: Option, +} + +impl Default for BuildOptions { + fn default() -> Self { + let current_exe = std::env::current_exe() + .expect("failed to get adept executable location") + .parent() + .expect("parent folder") + .to_path_buf(); + + let infrastructure = current_exe.join("infrastructure"); + + Self { + emit_llvm_ir: false, + emit_ir: false, + interpret: false, + coerce_main_signature: true, + excute_result: false, + allow_experimental_pragma_features: false, + use_pic: None, + target: Target::HOST, + infrastructure: Some(infrastructure), + } + } +} diff --git a/src/cli/build/parse.rs b/src/cli/build/parse.rs new file mode 100644 index 00000000..86d3e6b4 --- /dev/null +++ b/src/cli/build/parse.rs @@ -0,0 +1,58 @@ +use super::{options::BuildOptions, BuildCommand}; +use crate::target::{Target, TargetOs}; +use std::{path::PathBuf, str::FromStr}; + +impl BuildCommand { + pub fn parse(mut args: impl Iterator) -> Result { + let mut filename = None; + let mut options = BuildOptions::default(); + + while let Some(option) = args.next() { + match option.as_str() { + "-e" => options.excute_result = true, + "--emit-ir" => options.emit_ir = true, + "--emit-llvm-ir" => options.emit_llvm_ir = true, + "--interpret" => { + options.interpret = true; + options.coerce_main_signature = false; + } + "--windows" => { + options.target = Target::generic_os(TargetOs::Windows); + } + "--mac" | "--macos" => { + options.target = Target::generic_os(TargetOs::Mac); + } + "--linux" => { + options.target = Target::generic_os(TargetOs::Linux); + } + "--freebsd" => { + options.target = Target::generic_os(TargetOs::FreeBsd); + } + "--infrastructure" => { + let Some(infrastructure) = args.next() else { + eprintln!("error: Expected infrastructure path after '--infrastructure'"); + return Err(()); + }; + + options.infrastructure = Some( + PathBuf::from_str(&infrastructure) + .expect("invalid non-utf-8 infrastructure path"), + ); + } + _ => { + if filename.replace(option).is_some() { + eprintln!("error: Multiple paths specified"); + return Err(()); + } + } + } + } + + let Some(filename) = filename else { + eprintln!("error: No folder or filename specified"); + return Err(()); + }; + + Ok(Self { filename, options }) + } +} diff --git a/src/cli/help/invoke.rs b/src/cli/help/invoke.rs new file mode 100644 index 00000000..e2100663 --- /dev/null +++ b/src/cli/help/invoke.rs @@ -0,0 +1,9 @@ +use super::HelpCommand; +use crate::cli::CliInvoke; + +impl CliInvoke for HelpCommand { + fn invoke(self) -> Result<(), ()> { + println!("usage: adept FILENAME"); + Err(()) + } +} diff --git a/src/cli/help/mod.rs b/src/cli/help/mod.rs new file mode 100644 index 00000000..4ace2ed1 --- /dev/null +++ b/src/cli/help/mod.rs @@ -0,0 +1,5 @@ +mod invoke; +mod parse; + +#[derive(Clone, Debug)] +pub struct HelpCommand; diff --git a/src/cli/help/parse.rs b/src/cli/help/parse.rs new file mode 100644 index 00000000..ec41509b --- /dev/null +++ b/src/cli/help/parse.rs @@ -0,0 +1,7 @@ +use super::HelpCommand; + +impl HelpCommand { + pub fn parse(_: impl Iterator) -> Result { + Ok(Self) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs new file mode 100644 index 00000000..cb63a38d --- /dev/null +++ b/src/cli/mod.rs @@ -0,0 +1,34 @@ +mod build; +mod help; +mod new; + +use build::BuildCommand; +pub use build::BuildOptions; +use enum_dispatch::enum_dispatch; +use help::HelpCommand; +use new::NewCommand; + +#[enum_dispatch(CliInvoke)] +#[derive(Clone, Debug)] +pub enum CliCommand { + Help(HelpCommand), + Build(BuildCommand), + New(NewCommand), +} + +impl CliCommand { + pub fn parse() -> Result { + let mut args = std::env::args().skip(1).peekable(); + + match args.peek().map(String::as_str) { + Some("-h" | "--help") | None => HelpCommand::parse(args).map(Self::from), + Some("new") => NewCommand::parse(args).map(Self::from), + _ => BuildCommand::parse(args).map(Self::from), + } + } +} + +#[enum_dispatch] +pub trait CliInvoke { + fn invoke(self) -> Result<(), ()>; +} diff --git a/src/cli/new/invoke.rs b/src/cli/new/invoke.rs new file mode 100644 index 00000000..5d2067ad --- /dev/null +++ b/src/cli/new/invoke.rs @@ -0,0 +1,52 @@ +use super::NewCommand; +use crate::cli::CliInvoke; +use indoc::indoc; +use std::path::Path; + +impl CliInvoke for NewCommand { + fn invoke(self) -> Result<(), ()> { + if std::fs::create_dir(&self.project_name).is_err() { + eprintln!( + "error: Failed to create project directory '{}'", + &self.project_name + ); + return Err(()); + } + + let folder = Path::new(&self.project_name); + + put_file( + &folder.join("_.adept"), + indoc! {r#" + + pragma => { + adept("3.0") + } + "#}, + )?; + + put_file( + &folder.join("main.adept"), + indoc! {r#" + + func main { + println("Hello World!") + } + "#}, + )?; + + println!("Project created!"); + Ok(()) + } +} + +fn put_file(path: &Path, content: &str) -> Result<(), ()> { + std::fs::write(path, content).map_err(|_| { + let error_filename = path + .file_name() + .and_then(|filename| filename.to_str()) + .unwrap_or(""); + + eprintln!("error: Failed to create '{}' file", error_filename) + }) +} diff --git a/src/cli/new/mod.rs b/src/cli/new/mod.rs new file mode 100644 index 00000000..b260588b --- /dev/null +++ b/src/cli/new/mod.rs @@ -0,0 +1,7 @@ +mod invoke; +mod parse; + +#[derive(Clone, Debug)] +pub struct NewCommand { + pub project_name: String, +} diff --git a/src/cli/new/parse.rs b/src/cli/new/parse.rs new file mode 100644 index 00000000..4d875964 --- /dev/null +++ b/src/cli/new/parse.rs @@ -0,0 +1,15 @@ +use super::NewCommand; + +impl NewCommand { + pub fn parse(mut args: impl Iterator) -> Result { + // Skip over 'new' command keyword + args.next().unwrap(); + + let Some(project_name) = args.next() else { + println!("adept new "); + return Err(()); + }; + + Ok(Self { project_name }) + } +} diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs index 52cd373b..9a75e7df 100644 --- a/src/compiler/mod.rs +++ b/src/compiler/mod.rs @@ -13,7 +13,6 @@ use std::{ pub struct Compiler<'a> { pub options: BuildOptions, - pub target: &'a Target, pub source_files: &'a SourceFiles, pub diagnostics: &'a Diagnostics<'a>, pub version: OnceLock, @@ -22,6 +21,10 @@ pub struct Compiler<'a> { } impl<'a> Compiler<'a> { + pub fn target(&self) -> &Target { + &self.options.target + } + pub fn maybe_execute_result(&self, output_binary_filepath: &Path) -> Result<(), ()> { if !self.options.excute_result { return Ok(()); diff --git a/src/generate_workspace/mod.rs b/src/generate_workspace/mod.rs deleted file mode 100644 index 54fcc3a2..00000000 --- a/src/generate_workspace/mod.rs +++ /dev/null @@ -1,60 +0,0 @@ -/* - ======================= generate_workspace/mod.rs ======================= - Module for generating new workspaces - --------------------------------------------------------------------------- -*/ - -use crate::cli::NewCommand; -use indoc::indoc; -use std::{borrow::Borrow, fs, path::Path}; - -pub fn new_project(new_command: NewCommand) -> Result<(), ()> { - if std::fs::create_dir(&new_command.project_name).is_err() { - eprintln!( - "error: Failed to create project directory '{}'", - &new_command.project_name - ); - return Err(()); - } - - let folder = Path::new(&new_command.project_name); - - put_file( - folder.join("_.adept"), - indoc! {r#" - - pragma => { - adept("3.0") - } - "#}, - )?; - - put_file( - folder.join("main.adept"), - indoc! {r#" - - func main { - println("Hello World!") - } - "#}, - )?; - - println!("Project created!"); - Ok(()) -} - -fn put_file(path: impl Borrow, content: &str) -> Result<(), ()> { - let path = path.borrow(); - - if fs::write(path, content).is_err() { - let error_filename = path - .file_name() - .and_then(|filename| filename.to_str()) - .unwrap_or(""); - - eprintln!("error: Failed to create '{}' file", error_filename); - return Err(()); - } - - Ok(()) -} diff --git a/src/interpreter/mod.rs b/src/interpreter/mod.rs index 084ab8bf..4b12d934 100644 --- a/src/interpreter/mod.rs +++ b/src/interpreter/mod.rs @@ -22,7 +22,7 @@ pub use value::Value; pub struct Interpreter<'a, S: SyscallHandler> { pub syscall_handler: S, max_steps_left: Option, - ir_module: &'a ir::Module<'a>, + ir_module: &'a ir::Module, memory: Memory, global_addresses: HashMap, } @@ -103,7 +103,7 @@ impl<'a, S: SyscallHandler> Interpreter<'a, S> { self.call(call.function, arguments)? } ir::Instruction::Alloca(ty) => { - Value::Literal(self.memory.alloc_stack(self.size_of(ty))?) + Value::Literal(self.memory.alloc_stack(self.size_of(&ty))?) } ir::Instruction::Store(store) => { let new_value = self.eval(®isters, &store.new_value); @@ -113,7 +113,7 @@ impl<'a, S: SyscallHandler> Interpreter<'a, S> { Value::Undefined } ir::Instruction::Load((value, ty)) => { - let address = self.eval(®isters, value).as_u64().unwrap(); + let address = self.eval(®isters, &value).as_u64().unwrap(); self.memory.read(address, ty)? } ir::Instruction::Malloc(ir_type) => { diff --git a/src/ir/mod.rs b/src/ir/mod.rs index 8ee106fc..59c50ffa 100644 --- a/src/ir/mod.rs +++ b/src/ir/mod.rs @@ -15,14 +15,14 @@ use functions::Functions; use std::{collections::HashMap, ffi::CString}; pub use structures::{StructureRef, Structures}; -pub struct Module<'a> { - pub target: &'a Target, +pub struct Module { + pub target: Target, pub structures: Structures, pub globals: HashMap, pub functions: Functions, } -impl<'a> std::fmt::Debug for Module<'a> { +impl std::fmt::Debug for Module { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "SlotMap {{")?; for ir_function in self.functions.values() { @@ -469,8 +469,8 @@ pub struct ValueReference { pub instruction_id: usize, } -impl<'a> Module<'a> { - pub fn new(target: &'a Target) -> Self { +impl Module { + pub fn new(target: Target) -> Self { Self { target, functions: Functions::new(), diff --git a/src/llvm_backend/ctx.rs b/src/llvm_backend/ctx.rs index 48f78d25..a4d3363f 100644 --- a/src/llvm_backend/ctx.rs +++ b/src/llvm_backend/ctx.rs @@ -38,7 +38,7 @@ pub struct StructureCache { #[derive(Debug)] pub struct ToBackendTypeCtx<'a> { pub structure_cache: &'a StructureCache, - pub ir_module: &'a ir::Module<'a>, + pub ir_module: &'a ir::Module, pub visited: RefCell>, } @@ -58,7 +58,7 @@ pub struct FunctionSkeleton { pub struct BackendCtx<'a> { pub backend_module: &'a BackendModule, - pub ir_module: &'a ir::Module<'a>, + pub ir_module: &'a ir::Module, pub builder: Option, pub func_skeletons: HashMap, pub globals: HashMap, @@ -87,7 +87,7 @@ impl<'a> BackendCtx<'a> { diagnostics, ); - let arch = Arch::new(ir_module.target) + let arch = Arch::new(&ir_module.target) .ok_or_else(|| BackendError::plain("Target platform is not supported"))?; Ok(Self { diff --git a/src/llvm_backend/mod.rs b/src/llvm_backend/mod.rs index 284424dc..292c19b3 100644 --- a/src/llvm_backend/mod.rs +++ b/src/llvm_backend/mod.rs @@ -68,7 +68,7 @@ pub unsafe fn llvm_backend( let options = &compiler.options; let module_name = CString::new(output_object_filepath.to_str().expect("valid utf8")).unwrap(); - let triple = get_triple(&compiler.target)?; + let triple = get_triple(&options.target)?; let target = make_llvm_target(&triple)?; let cpu = CString::new("generic").unwrap(); let features = CString::new("").unwrap(); diff --git a/src/lower/expr/mod.rs b/src/lower/expr/mod.rs index ca1ad8bb..8454e486 100644 --- a/src/lower/expr/mod.rs +++ b/src/lower/expr/mod.rs @@ -284,7 +284,7 @@ pub fn lower_expr( let from_sign = cast_from .from_type .kind - .sign(Some(ir_module.target)) + .sign(Some(&ir_module.target)) .expect("integer to float must know sign"); let value = lower_expr(builder, ir_module, &cast.value, function, resolved_ast)?; @@ -782,11 +782,11 @@ pub fn lower_basic_binary_operation( })), resolved::BasicBinaryOperator::Divide(mode) => Ok(builder.push(ir::Instruction::Divide( operands, - mode.or_default_for(ir_module.target), + mode.or_default_for(&ir_module.target), ))), resolved::BasicBinaryOperator::Modulus(mode) => Ok(builder.push(ir::Instruction::Modulus( operands, - mode.or_default_for(ir_module.target), + mode.or_default_for(&ir_module.target), ))), resolved::BasicBinaryOperator::Equals(mode) => { Ok(builder.push(ir::Instruction::Equals(operands, *mode))) @@ -795,16 +795,16 @@ pub fn lower_basic_binary_operation( Ok(builder.push(ir::Instruction::NotEquals(operands, *mode))) } resolved::BasicBinaryOperator::LessThan(mode) => Ok(builder.push( - ir::Instruction::LessThan(operands, mode.or_default_for(ir_module.target)), + ir::Instruction::LessThan(operands, mode.or_default_for(&ir_module.target)), )), resolved::BasicBinaryOperator::LessThanEq(mode) => Ok(builder.push( - ir::Instruction::LessThanEq(operands, mode.or_default_for(ir_module.target)), + ir::Instruction::LessThanEq(operands, mode.or_default_for(&ir_module.target)), )), resolved::BasicBinaryOperator::GreaterThan(mode) => Ok(builder.push( - ir::Instruction::GreaterThan(operands, mode.or_default_for(ir_module.target)), + ir::Instruction::GreaterThan(operands, mode.or_default_for(&ir_module.target)), )), resolved::BasicBinaryOperator::GreaterThanEq(mode) => Ok(builder.push( - ir::Instruction::GreaterThanEq(operands, mode.or_default_for(ir_module.target)), + ir::Instruction::GreaterThanEq(operands, mode.or_default_for(&ir_module.target)), )), resolved::BasicBinaryOperator::BitwiseAnd => { Ok(builder.push(ir::Instruction::BitwiseAnd(operands))) diff --git a/src/lower/mod.rs b/src/lower/mod.rs index b45a1214..e4177e0c 100644 --- a/src/lower/mod.rs +++ b/src/lower/mod.rs @@ -14,18 +14,13 @@ use crate::{ ir::{self}, resolve::PolyRecipe, resolved, - target::Target, }; use function::{lower_function_body, lower_function_head}; use global::lower_global; use structure::lower_structure; -pub fn lower<'a>( - options: &BuildOptions, - rast: &resolved::Ast, - target: &'a Target, -) -> Result, LowerError> { - let mut ir_module = ir::Module::new(target); +pub fn lower<'a>(options: &BuildOptions, rast: &resolved::Ast) -> Result { + let mut ir_module = ir::Module::new(options.target.clone()); for (structure_ref, structure) in rast.structures.iter() { lower_structure(&mut ir_module, structure_ref, structure, rast)?; diff --git a/src/main.rs b/src/main.rs index ca689200..05b0bb76 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,6 @@ mod cli; mod compiler; mod data_units; mod diagnostics; -mod generate_workspace; mod index_map_ext; mod inflow; mod interpreter; @@ -44,131 +43,16 @@ mod tag; mod target; mod text; mod token; +mod unerror; mod version; mod workspace; -use crate::{cli::BuildCommand, show::Show, source_files::SourceFiles, text::IntoText}; -use cli::CommandKind; -use compiler::Compiler; -use diagnostics::{DiagnosticFlags, Diagnostics, WarningDiagnostic}; -use generate_workspace::new_project; -use single_file_only::compile_single_file_only; -use std::{fs::metadata, path::Path, process::ExitCode}; -use target::{TargetArch, TargetOs}; -use text::IntoTextStream; -use workspace::compile_workspace; +use cli::{CliCommand, CliInvoke}; +use std::process::ExitCode; fn main() -> ExitCode { - let Ok(args) = cli::Command::parse_env_args() else { - return ExitCode::FAILURE; - }; - - match match args.kind { - CommandKind::Build(build_command) => build_project(build_command), - CommandKind::New(new_command) => new_project(new_command), - } { + match CliCommand::parse().and_then(CliInvoke::invoke) { Ok(()) => ExitCode::SUCCESS, Err(()) => ExitCode::FAILURE, } } - -fn build_project(build_command: BuildCommand) -> Result<(), ()> { - let BuildCommand { filename, options } = build_command; - let source_files = SourceFiles::new(); - let filepath = Path::new(&filename); - let diagnostics = Diagnostics::new(&source_files, DiagnosticFlags::default()); - let target = options.target; - - let Ok(metadata) = metadata(filepath) else { - eprintln!("error: File or folder does not exist"); - return Err(()); - }; - - if target.arch().is_none() { - diagnostics.push(WarningDiagnostic::plain( - "Target architecture is not supported, falling back to best guess", - )); - } - - if target.os().is_none() { - diagnostics.push(WarningDiagnostic::plain( - "Target os is not supported, falling back to best guess", - )); - } - - match target.os().zip(target.arch()) { - Some((TargetOs::Windows, TargetArch::X86_64)) => (), - Some((TargetOs::Windows, TargetArch::Aarch64)) => (), - Some((TargetOs::Mac, TargetArch::X86_64)) => (), - Some((TargetOs::Mac, TargetArch::Aarch64)) => (), - Some((TargetOs::Linux, TargetArch::X86_64)) => (), - Some((TargetOs::Linux, TargetArch::Aarch64)) => (), - Some((TargetOs::FreeBsd, TargetArch::X86_64)) => (), - None => (), - #[allow(unreachable_patterns)] - _ => { - diagnostics.push(WarningDiagnostic::plain( - "Host os/architecture configuration is not officially supported, taking best guess", - )); - } - } - - let mut compiler = Compiler { - options, - target: &target, - source_files: &source_files, - diagnostics: &diagnostics, - version: Default::default(), - link_filenames: Default::default(), - link_frameworks: Default::default(), - }; - - if metadata.is_dir() { - return compile_workspace(&mut compiler, filepath, None); - } - - // Experimental header parsing - if filepath.extension().unwrap_or_default() == "h" { - let source_files = compiler.source_files; - - let content = std::fs::read_to_string(filepath).map_err(|err| { - eprintln!("{}", err); - () - })?; - - let header_key = source_files.add(filepath.into(), content); - - let header_contents = source_files - .get(header_key) - .content() - .chars() - .into_text_stream(header_key) - .into_text(); - - let preprocessed = unerror( - c::preprocessor::preprocess(header_contents, &diagnostics), - &source_files, - )?; - - println!("{preprocessed:?}"); - return Ok(()); - } - - let project_folder = filepath.parent().unwrap(); - compile_single_file_only(&mut compiler, project_folder, filepath) -} - -fn unerror(result: Result, source_files: &SourceFiles) -> Result { - match result { - Ok(value) => Ok(value), - Err(err) => { - let mut message = String::new(); - - err.show(&mut message, source_files) - .expect("show error message"); - - eprintln!("{message}"); - Err(()) - } - } -} diff --git a/src/pragma_section/run.rs b/src/pragma_section/run.rs index 58b327e6..e4e693c1 100644 --- a/src/pragma_section/run.rs +++ b/src/pragma_section/run.rs @@ -33,7 +33,6 @@ impl PragmaSection { target: Target::default(), infrastructure: None, }, - target: base_compiler.target, source_files: base_compiler.source_files, diagnostics: base_compiler.diagnostics, version: Default::default(), @@ -50,8 +49,7 @@ impl PragmaSection { let resolved_ast = resolve(&workspace, &compiler.options).map_err(into_show)?; - let ir_module = - lower(&compiler.options, &resolved_ast, &compiler.target).map_err(into_show)?; + let ir_module = lower(&compiler.options, &resolved_ast).map_err(into_show)?; let mut user_settings = run_build_system_interpreter(&resolved_ast, &ir_module) .map_err(|interpretter_error| { diff --git a/src/unerror.rs b/src/unerror.rs new file mode 100644 index 00000000..c536125f --- /dev/null +++ b/src/unerror.rs @@ -0,0 +1,16 @@ +use crate::{show::Show, source_files::SourceFiles}; + +pub fn unerror(result: Result, source_files: &SourceFiles) -> Result { + match result { + Ok(value) => Ok(value), + Err(err) => { + let mut message = String::new(); + + err.show(&mut message, source_files) + .expect("show error message"); + + eprintln!("{message}"); + Err(()) + } + } +} diff --git a/src/workspace/mod.rs b/src/workspace/mod.rs index 58863432..adfe36b4 100644 --- a/src/workspace/mod.rs +++ b/src/workspace/mod.rs @@ -27,7 +27,7 @@ use crate::{ show::Show, source_files::{Source, SourceFileKey}, token::Token, - unerror, + unerror::unerror, }; use compile::{ compile_code_file, @@ -255,7 +255,7 @@ pub fn compile_workspace( )?; let ir_module = unerror( - lower(&compiler.options, &resolved_ast, &compiler.target), + lower(&compiler.options, &resolved_ast), compiler.source_files, )?; @@ -274,13 +274,9 @@ pub fn compile_workspace( }); if compiler.options.interpret { - match run_build_system_interpreter(&resolved_ast, &ir_module) { - Ok(_) => return Ok(()), - Err(err) => { - eprintln!("{}", err); - return Err(()); - } - } + return run_build_system_interpreter(&resolved_ast, &ir_module) + .map(|_state| ()) + .map_err(|err| eprintln!("{}", err)); } let bin_folder = project_folder.join("bin"); @@ -289,8 +285,18 @@ pub fn compile_workspace( create_dir_all(&bin_folder).expect("failed to create bin folder"); create_dir_all(&obj_folder).expect("failed to create obj folder"); - let exe_filepath = bin_folder.join(compiler.target.default_executable_name(&project_name)); - let obj_filepath = obj_folder.join(compiler.target.default_object_file_name(&project_name)); + let exe_filepath = bin_folder.join( + compiler + .options + .target + .default_executable_name(&project_name), + ); + let obj_filepath = obj_folder.join( + compiler + .options + .target + .default_object_file_name(&project_name), + ); let linking_duration = unerror( unsafe { @@ -311,13 +317,11 @@ pub fn compile_workspace( let in_how_many_seconds = stats.seconds_elapsed(); let _linking_took = linking_duration.as_millis() as f64 / 1000.0; - // SAFETY: This is okay, as we synchronized by joining + // SAFETY: These are okay, as we synchronized by joining + let files_processed = stats.files_processed_estimate().separate_with_commas(); let bytes_processed = humansize::make_format(humansize::DECIMAL)(stats.bytes_processed_estimate()); - // SAFETY: This is okay, as we synchronized by joining - let files_processed = stats.files_processed_estimate().separate_with_commas(); - println!( "Compiled {} from {} files in {:.2} seconds", bytes_processed, files_processed, in_how_many_seconds,