diff --git a/Cargo.lock b/Cargo.lock index 4ada694..0cc26df 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -519,6 +519,7 @@ version = "0.3.7-alpha.1" dependencies = [ "mist-analyzer", "mist-api", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 8b37fa1..8a33dff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ include = [ [dependencies] mist-analyzer = { workspace = true } mist-api = { workspace = true } +toml = "1.1.2" [workspace] members = [ diff --git a/crates/mist-api/src/builder.mist b/crates/mist-api/src/builder.mist index 598886e..ea1d8af 100644 --- a/crates/mist-api/src/builder.mist +++ b/crates/mist-api/src/builder.mist @@ -58,7 +58,7 @@ pub bool build(Vec mut args, PathBuf root) serde_json::from_str(&map_str).expect("Failed to parse map file") }); let mist_file = map.mist_path.strip_prefix(&root); - let (_, rev_mapper::MistMap(line, column)) = map.find(&rev_mapper::RustMap(span.line_end, span.column_start)).expect("Unable to find mapping"); + let (_, rev_mapper::MistMap(line, column)) = map.find(&rev_mapper::RustMap(span.line_end, span.column_start)).unwrap_or_default(); let mist_msg = MistDiagnosticMessage { message: format!("{}: {}", msg.message.message, diff --git a/crates/mist-api/src/modules.mist b/crates/mist-api/src/modules.mist index bd9c29c..95fed90 100644 --- a/crates/mist-api/src/modules.mist +++ b/crates/mist-api/src/modules.mist @@ -76,7 +76,9 @@ pub class Module pub PathBuf output_path(&self, PathBuf& parent_dir, Option<(mist_parser::ast::Visibility, mist_parser::ast::Identifier)>& curr_mod_decl) { - let name = if let Some((_, name)) = curr_mod_decl { + let name = if self.path.is_dir() || self.is_package() { + &self.name + } else if let Some((_, name)) = curr_mod_decl { &name.0 } else { &self.name diff --git a/crates/mist-api/src/transpiler.mist b/crates/mist-api/src/transpiler.mist index 1e86bf2..7975dcf 100644 --- a/crates/mist-api/src/transpiler.mist +++ b/crates/mist-api/src/transpiler.mist @@ -136,12 +136,37 @@ impl modules::Module } (bool, String) write_transpile(&self, String output, PathBuf& parent_dir, bool child_changed) { - if self.path.is_dir() { + if self.path.is_dir() || self.is_package() { let output_file = self.output_path(parent_dir, &None); - if output.len() == 0 { - return (true, String::new()); - } - let res = fs::write(&output_file, output); + let final_output = if self.is_package() { + let source = match fs::read_to_string(&self.path) { + Ok(s) => s, + Err(e) => { + eprintln!("error: failed to read file {}\n {}", self.path.display(), e); + process::exit(1); + } + }; + let curr_mod_decl = mist_parser::parse_module(&source).expect("Failed parsing module"); + let (result_path, gc, rust_code) = match transpile_code(&self.path, &source, output_file.clone(), &output, &curr_mod_decl) { + Ok(v) => v, + Err(e) => { + decode_err(&e, &self.path); + process::exit(1); + } + }; + let map_path = result_path.with_extension("map.json"); + if let Some(parent) = map_path.parent() { + let _ = fs::create_dir_all(parent); + } + fs::write(&map_path, serde_json::to_string(&gc.mapping).unwrap()).expect("Failed to write map file"); + rust_code + } else { + if output.len() == 0 { + return (true, String::new()); + } + output + }; + let res = fs::write(&output_file, final_output); if res.is_err() { eprintln!("error: failed to write output {}\n {}", output_file.display(), @@ -222,13 +247,16 @@ pub void transpile_file(PathBuf& path, str& source, PathBuf output_file, str& mo process::exit(1); } }; + if let Some(parent) = output_file.parent() { + let _ = fs::create_dir_all(parent); + } let res = fs::write(&output_file, output); let output_file_map = output_file.with_extension("map.json"); let res_map = fs::write(&output_file_map, serde_json::to_string(&gc.mapping).unwrap()); if res_map.is_err() { eprintln!("error: failed to write mapping output {}\n {}", output_file_map.display(), - res.as_ref().unwrap_err(),); + res_map.as_ref().unwrap_err(),); } if res.is_err() { eprintln!("error: failed to write output {}\n {}", diff --git a/mist-test/src/common/decl_test.mist b/mist-test/src/common/decl_test.mist index 4f00a07..25cfa96 100644 --- a/mist-test/src/common/decl_test.mist +++ b/mist-test/src/common/decl_test.mist @@ -1,4 +1,5 @@ pub module decl_test; +pub module decl_test; pub void test_let_basic() { diff --git a/mist-test/src/common/expr_test.mist b/mist-test/src/common/expr_test.mist index c2f9fd7..0b3144a 100644 --- a/mist-test/src/common/expr_test.mist +++ b/mist-test/src/common/expr_test.mist @@ -1,4 +1,5 @@ pub module expr_test; +pub module expr_test; pub void test_literals() { diff --git a/mist-test/src/common/package.mist b/mist-test/src/common/package.mist index 3fb9503..da7a905 100644 --- a/mist-test/src/common/package.mist +++ b/mist-test/src/common/package.mist @@ -1,3 +1,4 @@ +pub module package; pub module common; pub void test_package() {} diff --git a/mist-test/src/common/statement_test.mist b/mist-test/src/common/statement_test.mist index aeab488..db498f1 100644 --- a/mist-test/src/common/statement_test.mist +++ b/mist-test/src/common/statement_test.mist @@ -1,4 +1,5 @@ pub module statement_test; +pub module statement_test; pub void test_var_decl() { diff --git a/mist-test/src/common/types_test.mist b/mist-test/src/common/types_test.mist index 76894b9..751cf5b 100644 --- a/mist-test/src/common/types_test.mist +++ b/mist-test/src/common/types_test.mist @@ -1,4 +1,5 @@ pub module types_test; +pub module types_test; pub void test_integer_type() { i32 x = 42; diff --git a/mist-test/src/items/attribute_test.mist b/mist-test/src/items/attribute_test.mist index 1d8cc07..eea1b62 100644 --- a/mist-test/src/items/attribute_test.mist +++ b/mist-test/src/items/attribute_test.mist @@ -1,4 +1,5 @@ pub module attribute_test; +pub module attribute_test; #[derive(Debug)] pub struct Debuggable diff --git a/mist-test/src/items/class_test.mist b/mist-test/src/items/class_test.mist index a0a0f5b..344a06b 100644 --- a/mist-test/src/items/class_test.mist +++ b/mist-test/src/items/class_test.mist @@ -1,4 +1,5 @@ pub module class_test; +pub module class_test; pub class Animal { diff --git a/mist-test/src/items/enum_test.mist b/mist-test/src/items/enum_test.mist index c46564a..0d3afff 100644 --- a/mist-test/src/items/enum_test.mist +++ b/mist-test/src/items/enum_test.mist @@ -1,4 +1,5 @@ pub module enum_test; +pub module enum_test; enum OptionInt { diff --git a/mist-test/src/items/function_test.mist b/mist-test/src/items/function_test.mist index 65b0515..08ce57d 100644 --- a/mist-test/src/items/function_test.mist +++ b/mist-test/src/items/function_test.mist @@ -1,4 +1,5 @@ pub module function_test; +pub module function_test; void greet() { diff --git a/mist-test/src/items/impl_test.mist b/mist-test/src/items/impl_test.mist index c28b869..01e71f4 100644 --- a/mist-test/src/items/impl_test.mist +++ b/mist-test/src/items/impl_test.mist @@ -1,4 +1,5 @@ pub module impl_test; +pub module impl_test; struct Vector2 { diff --git a/mist-test/src/items/struct_test.mist b/mist-test/src/items/struct_test.mist index 36e44fb..f6ef6c3 100644 --- a/mist-test/src/items/struct_test.mist +++ b/mist-test/src/items/struct_test.mist @@ -1,4 +1,5 @@ pub module struct_test; +pub module struct_test; struct Point { diff --git a/mist-test/src/items/trait_test.mist b/mist-test/src/items/trait_test.mist index c81c125..032b6a1 100644 --- a/mist-test/src/items/trait_test.mist +++ b/mist-test/src/items/trait_test.mist @@ -1,4 +1,5 @@ pub module trait_test; +pub module trait_test; trait Speak { diff --git a/src/main.mist b/src/main.mist index 3f28e86..6d17160 100644 --- a/src/main.mist +++ b/src/main.mist @@ -10,14 +10,15 @@ use mist_api::transpiler; void main() { - Vec args = env::args().collect(); + Vec mut args = env::args().collect(); if args.len() < 2 { print_usage(); process::exit(1); } match args[1].as_str() { - "run" | "build" | "check" | "r" | "b" | "c" => { - let root = env::current_dir().expect("Unable to find project root"); + "run" | "build" | "check" | "test" | "publish" | "bench" | "doc" | "fix" | "clippy" | "r" | "b" | "c" => { + let mut root = env::current_dir().expect("Unable to find project root"); + modify_args(&mut args, &mut root); transpiler::transpile(&root); println!(""); if builder::build(args, root) { @@ -27,12 +28,20 @@ void main() } } "transpile" | "t" => { - let root = env::current_dir().expect("Unable to find project root"); + let mut root = env::current_dir().expect("Unable to find project root"); + modify_args(&mut args, &mut root); transpiler::transpile(&root); } + "clean" => { + args.remove(0); + process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait"); + } "init" => { init_project(args); } + "new" => { + new_project(args); + } "version" | "--version" | "-v" => { println!("mist {}", env!("CARGO_PKG_VERSION")); } @@ -47,44 +56,123 @@ void main() } } +void modify_args(Vec mut& args, std::path::PathBuf mut& root) +{ + if let Some(path_arg) = args.get(2) { + if path_arg == "--path" { + if args.len() > 3 { + let path_arg = args[3].clone(); + *root = root.join(&path_arg); + args.remove(2); + args.remove(2); + args.push(format!("--manifest-path={}/Cargo.toml", root.display())); + } + } + } +} + void print_usage() { println!("Mist - the mist compiler\n"); - println!("󰖟 View our documentation! \x1b[35mhttps://mist.selimaj.dev\x1b[0m"); - println!(" Follow us on github! \x1b[35mhttps://github.com/mist-go\x1b[0m\n"); + println!("󰖟 View our documentation! \x1b[95mhttps://mist.selimaj.dev\x1b[0m"); + println!(" Follow us on github! \x1b[95mhttps://github.com/mist-go\x1b[0m\n"); println!("usage:"); - println!(" \x1b[36m mist run\x1b[0m,\x1b[36m r\x1b[0m run the project in the current directory\n"); - println!(" \x1b[36m mist build\x1b[0m,\x1b[36m b\x1b[0m build the project in the current directory\n"); - println!(" \x1b[36m mist transpile\x1b[0m,\x1b[36m t\x1b[0m transpile the project in the current directory\n"); - println!(" \x1b[36m mist check\x1b[0m,\x1b[36m c\x1b[0m check the project in the current directory\n"); - println!(" \x1b[36m mist init\x1b[0m initialize a project in the current folder\n"); - println!(" \x1b[36m mist version \x1b[0m print the compiler version\n"); - println!(" \x1b[36m mist help\x1b[0m print this message\n"); + println!(" \x1b[96m mist run\x1b[0m,\x1b[96m r\x1b[0m run the project\n"); + println!(" \x1b[96m mist build\x1b[0m,\x1b[96m b\x1b[0m build the project\n"); + println!(" \x1b[96m mist test\x1b[0m test the project\n"); + println!(" \x1b[96m mist publish\x1b[0m publish the project\n"); + println!(" \x1b[96m mist bench\x1b[0m benchmark the project\n"); + println!(" \x1b[96m mist doc\x1b[0m build documentation\n"); + println!(" \x1b[96m mist fix\x1b[0m automatically fix warnings\n"); + println!(" \x1b[96m mist clippy\x1b[0m lint the project\n"); + println!(" \x1b[96m mist check\x1b[0m,\x1b[96m c\x1b[0m check the project\n"); + println!(" \x1b[96m mist clean\x1b[0m clean build artifacts\n"); + println!(" \x1b[96m mist transpile\x1b[0m,\x1b[96m t\x1b[0m transpile the project\n"); + println!(" \x1b[96m mist init\x1b[0m initialize a project in the current folder\n"); + println!(" \x1b[96m mist new\x1b[0m create a new project\n"); + println!(" \x1b[96m mist version \x1b[0m print the compiler version\n"); + println!(" \x1b[96m mist help\x1b[0m print this message\n"); } void init_project(Vec mut args) { args.remove(0); - process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait"); + let status = process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait"); + if !status.success() { + return; + } bool is_lib = fs::metadata("src/lib.rs").is_ok(); if is_lib { fs::remove_file("src/lib.rs").expect("Failed to remove src/lib.rs"); - fs::write("src/lib.mist", "fn test() {\n}").expect("Failed to create src/lib.mist"); + fs::write("src/lib.mist", "void test() {\n}").expect("Failed to create src/lib.mist"); } else { fs::remove_file("src/main.rs").expect("Failed to remove src/main.rs"); - fs::write("src/main.mist", "fn main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist"); + fs::write("src/main.mist", "void main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist"); } inject_mist_target(is_lib); - println!("\n\x1b[34minfo\x1b[0m: Please open Cargo.toml and adjust `\x1b[35mname\x1b[0m = ` as needed, Thank you for using Mist!\n"); + add_mist_toml(is_lib); + add_gitignore(); + println!("Thank you for using Mist!\n"); +} + +void new_project(Vec mut args) +{ + args.remove(0); + if args.len() < 2 { + eprintln!("error: missing project name\n"); + print_usage(); + process::exit(1); + } + String project_name = args[1].clone(); + let status = process::Command::new("cargo").args(args).stdout(process::Stdio::inherit()).stderr(process::Stdio::inherit()).stdin(process::Stdio::inherit()).spawn().expect("Failed to run cargo").wait().expect("Unable to wait"); + if !status.success() { + return; + } + std::path::PathBuf project_path = env::current_dir().expect("Unable to find project root").join(&project_name); + env::set_current_dir(&project_path).expect("Failed to change to project directory"); + bool is_lib = fs::metadata("src/lib.rs").is_ok(); + if is_lib { + fs::remove_file("src/lib.rs").expect("Failed to remove src/lib.rs"); + fs::write("src/lib.mist", "void test() {\n}").expect("Failed to create src/lib.mist"); + } else { + fs::remove_file("src/main.rs").expect("Failed to remove src/main.rs"); + fs::write("src/main.mist", "void main() {\n println!(\"Hello, World!\");\n}\n").expect("Failed to create src/main.mist"); + } + inject_mist_target(is_lib); + add_mist_toml(is_lib); + add_gitignore(); + println!("Thank you for using Mist!\n"); } void inject_mist_target(bool is_lib) { String toml_content = fs::read_to_string("Cargo.toml").expect("Failed to read Cargo.toml"); - str& inject = if is_lib { - "\n[lib]\nname = \"my_lib\"\npath = \".mist/src/lib.rs\"\n" + toml::Value parsed = toml::from_str(&toml_content).expect("Failed to parse Cargo.toml"); + String name = parsed["package"]["name"].as_str().expect("Missing package.name in Cargo.toml").to_string(); + String inject = if is_lib { + format!("\n[lib]\nname = \"{name}\"\npath = \".mist/src/lib.rs\"\n") } else { - "\n[[bin]]\nname = \"my_bin\"\npath = \".mist/src/main.rs\"\n" + format!("\n[[bin]]\nname = \"{name}\"\npath = \".mist/src/main.rs\"\n") }; - fs::write("Cargo.toml", toml_content + inject).expect("Failed to inject to Cargo.toml"); + fs::write("Cargo.toml", toml_content + &inject).expect("Failed to inject to Cargo.toml"); +} + +void add_mist_toml(bool is_lib) +{ + str& content = if is_lib { + "package = \"lib.mist\"\npackages = []\ninclude = []\n" + } else { + "package = \"main.mist\"\npackages = []\ninclude = []\n" + }; + fs::write("Mist.toml", content).expect("Failed to add Mist.toml"); +} + +void add_gitignore() +{ + if fs::metadata(".gitignore").is_ok() { + String content = fs::read_to_string(".gitignore").expect("Failed to read .gitignore"); + if !content.contains(".mist") { + fs::write(".gitignore", content + "\n.mist\n").expect("Failed to write .gitignore"); + } + } }