diff --git a/.gitignore b/.gitignore index 4709b0c..2a39a6a 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ target/ *.pdb # Generated by the compiler -/*.cpp -/*.out \ No newline at end of file +/*.ts \ No newline at end of file diff --git a/Makefile b/Makefile deleted file mode 100644 index 2e718bc..0000000 --- a/Makefile +++ /dev/null @@ -1,11 +0,0 @@ -build-debug: - @echo "Building executable (debug)... done" - cargo build - cp ./target/debug/hazure ~/bin/hazure -r - @echo "Building executable (debug)... done" - -build-lib: - @echo "Building lib..." - rm -rf /usr/include/hazure/ - cp ./lib/. /usr/include/hazure/ -r - @echo "Building lib... done" \ No newline at end of file diff --git a/README.md b/README.md index 2748837..2ff65ab 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,25 @@ # Hazure -Programming language that compiles to C++! +Programming language that compiles to Typescript! ```sml -fun main: int = do +fun main: void = do @write("Hello, World!\n"); - return 69; end; ``` or with the pipe operator: ```sml -fun main: int = do +fun main: void = do "Hello, World!\n" |> @write(_); - return 69; end; ``` -> The `return 69` is the exit code (like C++), try running `echo $?` to see it! Note: Everything in this project can be changed at anytime! (I'm still finding out what work best for lots of thing) if you have an idea, feel free to create an issues about it, or even create a PR! (I'd be very happy) +# Prerequistie +- `deno` +- Rust (if you're going to build from source) + # Contributing Found a bug? Found a better way to do something? Make a pull request or tell me in the issues tab! Anything contributions helps :D @@ -26,9 +27,8 @@ Wanna see how it works under the hood? see the [How it works](https://github.com Steps to build: 1) Clone this repo `https://github.com/azur1s/hazure.git` -2) Run `sudo make build-lib` to build the library (for the transpiled output) -3) Build executable `cargo build` -4) Try running some examples! `path/to/executable compile path/to/file.hz` +2) Build executable `cargo build` +3) Try running some examples! `path/to/executable compile path/to/file.hz` # How it works ``` @@ -62,25 +62,12 @@ Steps to build: Pass │ │ - Command Codegen produce C++ - (spawn) │ crates/codegen - │ │ - │ │ - clang++ ─────┴───── Executable -(Command) -``` - -# Prerequistie -- `clang++`(preferred, default) or any C++ compiler -- `make` for Makefile -- Rust (if you're going to build from source) - -# Configuration -You can also configurate Hades compiler (currently you can only change the C++ compiler). Make a new file called `hades.toml` in the current working directory and the compiler will look for it! if there isn't one then it will use the default configuration: -```toml -[compiler] -compiler = "clang++" + Codegen produce Typescript + │ crates/codegen + │ + Done + (filename.ts) ``` # License -Hades is licensed under both [MIT license](https://github.com/azur1s/hades/blob/master/LICENSE-MIT) and [Apache License](https://github.com/azur1s/hades/blob/master/LICENSE-APACHE) \ No newline at end of file +Hazure is licensed under both [MIT license](https://github.com/azur1s/hazure/blob/master/LICENSE-MIT) and [Apache License](https://github.com/azur1s/hazure/blob/master/LICENSE-APACHE) \ No newline at end of file diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs index 83fc028..31f74a6 100644 --- a/crates/codegen/src/lib.rs +++ b/crates/codegen/src/lib.rs @@ -1 +1 @@ -pub mod cpp; \ No newline at end of file +pub mod ts; \ No newline at end of file diff --git a/crates/codegen/src/cpp.rs b/crates/codegen/src/ts.rs similarity index 79% rename from crates/codegen/src/cpp.rs rename to crates/codegen/src/ts.rs index 8e7862f..4a2e1ff 100644 --- a/crates/codegen/src/cpp.rs +++ b/crates/codegen/src/ts.rs @@ -2,13 +2,6 @@ use std::fmt::Display; use hir::{IR, IRKind, Value}; -const MODULE_INCLUDES: [&str; 4] = [ - "\"hazure/io.hpp\"", - "\"hazure/time.hpp\"", - "", - "", -]; - pub struct Codegen { pub emitted: String, } @@ -25,23 +18,23 @@ impl Codegen { pub fn gen(&mut self, irs: Vec) { self.emit(format!("// Auto-generated by hazure compiler version {}\n", env!("CARGO_PKG_VERSION"))); - for module in MODULE_INCLUDES { - self.emit(format!("#include {}\n", module)); - } for ir in irs { self.emit(&self.gen_ir(&ir.kind, true)); } + + self.emit("main();"); } fn gen_ir(&self, ir: &IRKind, should_gen_semicolon: bool) -> String { #[macro_export] macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; } + match ir { IRKind::Define { name, type_hint, value } => { format!( - "{} {} = {}{}\n", - type_hint, + "const {}: {} = {}{}\n", name, + type_hint, self.gen_ir(value, false), semicolon!() ) @@ -63,13 +56,8 @@ impl Codegen { IRKind::Intrinsic { name, args } => { match name.as_str() { - "write" => { format!("hazure_write({}){}\n", self.gen_ir(&args[0], false), semicolon!()) }, - "read" => { format!("hazure_read(){}\n", semicolon!()) }, - - "write_file" => { format!("hazure_write({}){}\n", self.gen_ir(&args[0], false), semicolon!()) }, - "read_file" => { format!("hazure_read_file({}){}\n", self.gen_ir(&args[0], false), semicolon!()) }, - - "time" => { format!("hazure_get_time(){}\n", semicolon!()) }, + "write" => { format!("console.log({}){}\n", self.gen_ir(&args[0], false), semicolon!()) }, + "read" => { todo!() }, _ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering } }, @@ -77,14 +65,14 @@ impl Codegen { IRKind::Fun { name, return_type_hint, args, body } => { let args = args .iter() - .map(|arg| format!("{} {}", arg.1, arg.0)) + .map(|arg| format!("{}: {}", arg.0, arg.1)) .collect::>(). join(", "); format!( - "{} {}({}) {{\n{}\n}}\n", - return_type_hint, + "const {} = ({}): {} => {{\n{}\n}};\n", name, args, + return_type_hint, self.gen_ir(body, false) ) }, diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index f4df607..da98155 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1,12 +1,9 @@ use std::ops::Range; use parser::Expr; -const INTRINSICS: [&str; 5] = [ +const INTRINSICS: [&str; 2] = [ "write", "read", - "write_file", - "read_file", - "time", ]; #[derive(Debug, Clone)] @@ -285,9 +282,10 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { fn gen_type_hint(type_hint: &str) -> String { match type_hint { - "int" => "int".to_string(), - "bool" => "bool".to_string(), - "string" => "std::string".to_string(), + "int" => "number".to_string(), + "bool" => "boolean".to_string(), + "string" => "string".to_string(), + "void" => "void".to_string(), _ => { dbg!(type_hint); todo!() } } } diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index e470253..6823476 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -13,7 +13,7 @@ pub enum Token { String(String), Identifier(String), // Operators - Plus, Minus, Multiply, Divide, + Plus, Minus, Multiply, Divide, Modulus, Not, Equal, NotEqual, Less, Greater, Pipe, @@ -47,6 +47,7 @@ impl std::fmt::Display for Token { Token::Minus => write!(f, "-"), Token::Multiply => write!(f, "*"), Token::Divide => write!(f, "/"), + Token::Modulus => write!(f, "%"), Token::Not => write!(f, "!"), Token::Equal => write!(f, "=="), Token::NotEqual => write!(f, "!="), diff --git a/crates/main/src/config.rs b/crates/main/src/config.rs deleted file mode 100644 index 4407414..0000000 --- a/crates/main/src/config.rs +++ /dev/null @@ -1,25 +0,0 @@ -use serde_derive::Deserialize; -use toml::{from_str, de::Error}; - -#[derive(Deserialize)] -pub struct Config { - pub compiler: CompilerOptions, -} - -#[derive(Deserialize)] -pub struct CompilerOptions { - pub compiler: String, -} - -pub fn default_config() -> Config { - Config { - compiler: CompilerOptions { - compiler: "clang++".to_string(), - }, - } -} - -pub fn parse_config(toml_content: &str) -> Result { - let config: Config = from_str(toml_content)?; - Ok(config) -} \ No newline at end of file diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index 94ed125..7663578 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -6,43 +6,22 @@ use lexer::lex; use parser::parse; use diagnostic::Diagnostics; use hir::ast_to_ir; -use codegen::cpp; +use codegen::ts; pub mod args; use args::{Args, Options}; -pub mod config; - pub mod util; use crate::util::log; fn main() { - let config_file = fs::read_to_string("./hazure.toml"); - let config: config::Config; - match config_file { - Ok(content) => { - let parsed = config::parse_config(&content); - match parsed { - Ok(c) => config = c, - Err(e) => { - log(2, format!("{}", e)); - config = config::default_config(); - } - } - } - Err(e) => { - log(1, format!("Error reading config file: {}, using default config", e)); - config = config::default_config(); - } - } - let args = Args::parse(); match args.options { Options::Compile { input: file_name, ast: print_ast, log: should_log, - output, + output: _output, // TODO: Custom output file } => { // Macro to only log if `should_log` is true macro_rules! logif { @@ -93,37 +72,26 @@ fn main() { } // Generate code - let mut codegen = cpp::Codegen::new(); + let mut codegen = ts::Codegen::new(); codegen.gen(ir); logif!(0, "Successfully generated code."); // Write code to file - let output_path: PathBuf = file_name.with_extension("cpp").file_name().unwrap().to_os_string().into(); + let output_path: PathBuf = file_name.with_extension("ts").file_name().unwrap().to_os_string().into(); let mut file = fs::File::create(&output_path).expect("Failed to create file"); file.write_all(codegen.emitted.as_bytes()).expect("Failed to write to file"); - // Compile the generate code - let compiler = &config.compiler.compiler; - Command::new(compiler) - .arg(&output_path) - .arg(format!("-o{}", match output { - Some(o) => o.display().to_string(), - None => file_name - .with_extension("out") - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(), - })) - .spawn() - .expect("Failed to run compiler"); - // End timer let duration = start.elapsed().as_millis(); logif!(0, format!("Compilation took {}ms", duration)); logif!(0, format!("Wrote output to `{}`", output_path.display())); + + Command::new("chmod") + .arg("+x") + .arg(&output_path) + .spawn() + .expect("Failed to chmod file"); }, None => { unreachable!(); } } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 18a3cec..15908e8 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -124,7 +124,8 @@ fn expr_parser() -> impl Parser>, Error = Simple .then( choice(( just(Token::Multiply), - just(Token::Divide))) + just(Token::Divide), + just(Token::Modulus))) .then(unary) .repeated()) .foldl(|lhs, (op, rhs)| { diff --git a/example/69.hz b/example/69.hz index 0602656..bc8399a 100644 --- a/example/69.hz +++ b/example/69.hz @@ -2,10 +2,9 @@ fun add2 (lhs: int) (rhs: int): int = do return lhs + rhs; end; -fun main: int = do +fun main: void = do let result: int = add2(34, 35); @write(result); - @write("\n"); if result == 69 then @write("big cool") else diff --git a/example/err/unknown_intrinsic.hz b/example/err/unknown_intrinsic.hz index cdf2c48..515349d 100644 --- a/example/err/unknown_intrinsic.hz +++ b/example/err/unknown_intrinsic.hz @@ -1,3 +1,3 @@ -fun main: int = do +fun main: void = do @writse(); -- Unknown intrinsic end; \ No newline at end of file diff --git a/example/hello_world.hz b/example/hello_world.hz index 5fddf4f..a014ee4 100644 --- a/example/hello_world.hz +++ b/example/hello_world.hz @@ -1,5 +1,3 @@ -fun main: int = do - -- Normal way of hello world - @write("Hello, World!\n"); - return 69; +fun main: void = do + @write("Hello, World!"); end; \ No newline at end of file diff --git a/example/if.hz b/example/if.hz index d9a5054..2f2f7cd 100644 --- a/example/if.hz +++ b/example/if.hz @@ -1,4 +1,4 @@ -fun main: int = do +fun main: void = do if true then @write("True") else diff --git a/example/pipe.hz b/example/pipe.hz index fd8d3e1..b373e74 100644 --- a/example/pipe.hz +++ b/example/pipe.hz @@ -1,13 +1,11 @@ fun foo (xs: int): int = return xs + 1; fun bar (xs: int) (x: int): int = return xs - x; -fun main: int = do +fun main: void = do foo(69) -- 69 + 1 => 70 |> bar(_, 1) -- '70 - 1 => 69 |> @write(_); -- '69 => stdout - @write("\n"); - foo(60) -- 60 + 1 => 61 |> bar(130, _) -- 130 - '61 => 69 |> @write(_); -- '69 => stdout diff --git a/example/pipe2.hz b/example/pipe2.hz new file mode 100644 index 0000000..197cb38 --- /dev/null +++ b/example/pipe2.hz @@ -0,0 +1,15 @@ +fun foo (xs: int) : int = return xs + 1; +fun bar (xs: int) (ys: int) : int = return xs + ys; +fun baz (xs: int) (ys: int) (zs: int): int = return xs + ys + zs; +fun qux (xs: int) : int = return xs - 1; +fun quux (xs: int) (xy: int) : int = return xs - 2 + xy; + +fun main: void = do + 66 + |> foo(_) + |> bar(_, 1) + |> baz(1, 2, _) + |> qux(_) + |> quux(1, _) + |> @write(_); +end; \ No newline at end of file diff --git a/example/quine.hz b/example/quine.hz deleted file mode 100644 index 2c300d2..0000000 --- a/example/quine.hz +++ /dev/null @@ -1,4 +0,0 @@ -fun main: int = do - @read_file("./example/quine.hz") -- Run this from root folder - |> @write(_); -end; \ No newline at end of file diff --git a/example/readwrite.hz b/example/readwrite.hz deleted file mode 100644 index 4f394aa..0000000 --- a/example/readwrite.hz +++ /dev/null @@ -1,9 +0,0 @@ -fun main: int = do - @write("Enter your name: "); - let name: string = ""; - @read(name); - - @write("Hello "); - @write(name); - @write("!"); -end; \ No newline at end of file diff --git a/example/time.hz b/example/time.hz deleted file mode 100644 index a9efd71..0000000 --- a/example/time.hz +++ /dev/null @@ -1,4 +0,0 @@ -fun main: int = do - @time() - |> @write(_); -end; \ No newline at end of file diff --git a/hazure.toml b/hazure.toml deleted file mode 100644 index bf19bbb..0000000 --- a/hazure.toml +++ /dev/null @@ -1,2 +0,0 @@ -[compiler] -compiler = "clang++" \ No newline at end of file diff --git a/lib/io.hpp b/lib/io.hpp deleted file mode 100644 index b8780de..0000000 --- a/lib/io.hpp +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once -#include -#include -#include - -template -/** - * Read the value from stdin and return it. - */ -T hazure_read() { - T x; - std::cin >> x; - return x; -} - -template -/** - * Prints the value of the variable to the stdout. - * - * @param value The value to print. - */ -void hazure_write(T x) { - std::cout << x; -} - -/* - * Read the value from the file and return it. - * - * @param file_name The name of the file to read from. - * @return std::string The value read from the file. - */ -std::string hazure_read_file(std::string filename) { - std::ifstream file(filename); - std::string content((std::istreambuf_iterator(file)), - (std::istreambuf_iterator())); - return content; -} - -/* - * Write string to file. - * - * @param filename The file name to write to. - * @param content The content to write. - */ -void hazure_write_file(std::string filename, std::string content) { - std::ofstream file(filename); - if (file.is_open()) { - file << content; - file.close(); - } else { - std::cerr << "Unable to open " << filename << std::endl; - } -} \ No newline at end of file diff --git a/lib/time.hpp b/lib/time.hpp deleted file mode 100644 index 3533196..0000000 --- a/lib/time.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once -#include - -/* - * @brief Get time in seconds since the Epoch. - */ -int hazure_get_time() { - return std::time(0); -} \ No newline at end of file