1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

cpp -> typescript

This commit is contained in:
Natapat Samutpong 2022-03-16 07:36:39 +07:00
parent 24dbfc23ff
commit 71f313dbe1
22 changed files with 67 additions and 232 deletions

3
.gitignore vendored
View file

@ -10,5 +10,4 @@ target/
*.pdb *.pdb
# Generated by the compiler # Generated by the compiler
/*.cpp /*.ts
/*.out

View file

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

View file

@ -1,24 +1,25 @@
# Hazure # Hazure
Programming language that compiles to C++! Programming language that compiles to Typescript!
```sml ```sml
fun main: int = do fun main: void = do
@write("Hello, World!\n"); @write("Hello, World!\n");
return 69;
end; end;
``` ```
or with the pipe operator: or with the pipe operator:
```sml ```sml
fun main: int = do fun main: void = do
"Hello, World!\n" "Hello, World!\n"
|> @write(_); |> @write(_);
return 69;
end; 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) 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 # 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 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: Steps to build:
1) Clone this repo `https://github.com/azur1s/hazure.git` 1) Clone this repo `https://github.com/azur1s/hazure.git`
2) Run `sudo make build-lib` to build the library (for the transpiled output) 2) Build executable `cargo build`
3) Build executable `cargo build` 3) Try running some examples! `path/to/executable compile path/to/file.hz`
4) Try running some examples! `path/to/executable compile path/to/file.hz`
# How it works # How it works
``` ```
@ -62,25 +62,12 @@ Steps to build:
Pass Pass
Command Codegen produce C++ Codegen produce Typescript
(spawn) │ crates/codegen │ crates/codegen
│ │
│ │ Done
clang++ ─────┴───── Executable (filename.ts)
(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++"
``` ```
# License # 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) 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)

View file

@ -1 +1 @@
pub mod cpp; pub mod ts;

View file

@ -2,13 +2,6 @@ use std::fmt::Display;
use hir::{IR, IRKind, Value}; use hir::{IR, IRKind, Value};
const MODULE_INCLUDES: [&str; 4] = [
"\"hazure/io.hpp\"",
"\"hazure/time.hpp\"",
"<stdbool.h>",
"<string>",
];
pub struct Codegen { pub struct Codegen {
pub emitted: String, pub emitted: String,
} }
@ -25,23 +18,23 @@ impl Codegen {
pub fn gen(&mut self, irs: Vec<IR>) { pub fn gen(&mut self, irs: Vec<IR>) {
self.emit(format!("// Auto-generated by hazure compiler version {}\n", env!("CARGO_PKG_VERSION"))); 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 { for ir in irs {
self.emit(&self.gen_ir(&ir.kind, true)); self.emit(&self.gen_ir(&ir.kind, true));
} }
self.emit("main();");
} }
fn gen_ir(&self, ir: &IRKind, should_gen_semicolon: bool) -> String { fn gen_ir(&self, ir: &IRKind, should_gen_semicolon: bool) -> String {
#[macro_export] #[macro_export]
macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; } macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; }
match ir { match ir {
IRKind::Define { name, type_hint, value } => { IRKind::Define { name, type_hint, value } => {
format!( format!(
"{} {} = {}{}\n", "const {}: {} = {}{}\n",
type_hint,
name, name,
type_hint,
self.gen_ir(value, false), self.gen_ir(value, false),
semicolon!() semicolon!()
) )
@ -63,13 +56,8 @@ impl Codegen {
IRKind::Intrinsic { name, args } => { IRKind::Intrinsic { name, args } => {
match name.as_str() { match name.as_str() {
"write" => { format!("hazure_write({}){}\n", self.gen_ir(&args[0], false), semicolon!()) }, "write" => { format!("console.log({}){}\n", self.gen_ir(&args[0], false), semicolon!()) },
"read" => { format!("hazure_read(){}\n", semicolon!()) }, "read" => { todo!() },
"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!()) },
_ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering _ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering
} }
}, },
@ -77,14 +65,14 @@ impl Codegen {
IRKind::Fun { name, return_type_hint, args, body } => { IRKind::Fun { name, return_type_hint, args, body } => {
let args = args let args = args
.iter() .iter()
.map(|arg| format!("{} {}", arg.1, arg.0)) .map(|arg| format!("{}: {}", arg.0, arg.1))
.collect::<Vec<_>>(). .collect::<Vec<_>>().
join(", "); join(", ");
format!( format!(
"{} {}({}) {{\n{}\n}}\n", "const {} = ({}): {} => {{\n{}\n}};\n",
return_type_hint,
name, name,
args, args,
return_type_hint,
self.gen_ir(body, false) self.gen_ir(body, false)
) )
}, },

View file

@ -1,12 +1,9 @@
use std::ops::Range; use std::ops::Range;
use parser::Expr; use parser::Expr;
const INTRINSICS: [&str; 5] = [ const INTRINSICS: [&str; 2] = [
"write", "write",
"read", "read",
"write_file",
"read_file",
"time",
]; ];
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -285,9 +282,10 @@ pub fn expr_to_ir(expr: &Expr) -> (Option<IRKind>, Option<LoweringError>) {
fn gen_type_hint(type_hint: &str) -> String { fn gen_type_hint(type_hint: &str) -> String {
match type_hint { match type_hint {
"int" => "int".to_string(), "int" => "number".to_string(),
"bool" => "bool".to_string(), "bool" => "boolean".to_string(),
"string" => "std::string".to_string(), "string" => "string".to_string(),
"void" => "void".to_string(),
_ => { dbg!(type_hint); todo!() } _ => { dbg!(type_hint); todo!() }
} }
} }

View file

@ -13,7 +13,7 @@ pub enum Token {
String(String), Identifier(String), String(String), Identifier(String),
// Operators // Operators
Plus, Minus, Multiply, Divide, Plus, Minus, Multiply, Divide, Modulus,
Not, Equal, NotEqual, Less, Greater, Not, Equal, NotEqual, Less, Greater,
Pipe, Pipe,
@ -47,6 +47,7 @@ impl std::fmt::Display for Token {
Token::Minus => write!(f, "-"), Token::Minus => write!(f, "-"),
Token::Multiply => write!(f, "*"), Token::Multiply => write!(f, "*"),
Token::Divide => write!(f, "/"), Token::Divide => write!(f, "/"),
Token::Modulus => write!(f, "%"),
Token::Not => write!(f, "!"), Token::Not => write!(f, "!"),
Token::Equal => write!(f, "=="), Token::Equal => write!(f, "=="),
Token::NotEqual => write!(f, "!="), Token::NotEqual => write!(f, "!="),

View file

@ -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<Config, Error> {
let config: Config = from_str(toml_content)?;
Ok(config)
}

View file

@ -6,43 +6,22 @@ use lexer::lex;
use parser::parse; use parser::parse;
use diagnostic::Diagnostics; use diagnostic::Diagnostics;
use hir::ast_to_ir; use hir::ast_to_ir;
use codegen::cpp; use codegen::ts;
pub mod args; pub mod args;
use args::{Args, Options}; use args::{Args, Options};
pub mod config;
pub mod util; pub mod util;
use crate::util::log; use crate::util::log;
fn main() { 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(); let args = Args::parse();
match args.options { match args.options {
Options::Compile { Options::Compile {
input: file_name, input: file_name,
ast: print_ast, ast: print_ast,
log: should_log, log: should_log,
output, output: _output, // TODO: Custom output file
} => { } => {
// Macro to only log if `should_log` is true // Macro to only log if `should_log` is true
macro_rules! logif { macro_rules! logif {
@ -93,37 +72,26 @@ fn main() {
} }
// Generate code // Generate code
let mut codegen = cpp::Codegen::new(); let mut codegen = ts::Codegen::new();
codegen.gen(ir); codegen.gen(ir);
logif!(0, "Successfully generated code."); logif!(0, "Successfully generated code.");
// Write code to file // 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"); 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"); 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 // End timer
let duration = start.elapsed().as_millis(); let duration = start.elapsed().as_millis();
logif!(0, format!("Compilation took {}ms", duration)); logif!(0, format!("Compilation took {}ms", duration));
logif!(0, format!("Wrote output to `{}`", output_path.display())); 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!(); } None => { unreachable!(); }
} }

View file

@ -124,7 +124,8 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
.then( .then(
choice(( choice((
just(Token::Multiply), just(Token::Multiply),
just(Token::Divide))) just(Token::Divide),
just(Token::Modulus)))
.then(unary) .then(unary)
.repeated()) .repeated())
.foldl(|lhs, (op, rhs)| { .foldl(|lhs, (op, rhs)| {

View file

@ -2,10 +2,9 @@ fun add2 (lhs: int) (rhs: int): int = do
return lhs + rhs; return lhs + rhs;
end; end;
fun main: int = do fun main: void = do
let result: int = add2(34, 35); let result: int = add2(34, 35);
@write(result); @write(result);
@write("\n");
if result == 69 then if result == 69 then
@write("big cool") @write("big cool")
else else

View file

@ -1,3 +1,3 @@
fun main: int = do fun main: void = do
@writse(); -- Unknown intrinsic @writse(); -- Unknown intrinsic
end; end;

View file

@ -1,5 +1,3 @@
fun main: int = do fun main: void = do
-- Normal way of hello world @write("Hello, World!");
@write("Hello, World!\n");
return 69;
end; end;

View file

@ -1,4 +1,4 @@
fun main: int = do fun main: void = do
if true then if true then
@write("True") @write("True")
else else

View file

@ -1,13 +1,11 @@
fun foo (xs: int): int = return xs + 1; fun foo (xs: int): int = return xs + 1;
fun bar (xs: int) (x: int): int = return xs - x; fun bar (xs: int) (x: int): int = return xs - x;
fun main: int = do fun main: void = do
foo(69) -- 69 + 1 => 70 foo(69) -- 69 + 1 => 70
|> bar(_, 1) -- '70 - 1 => 69 |> bar(_, 1) -- '70 - 1 => 69
|> @write(_); -- '69 => stdout |> @write(_); -- '69 => stdout
@write("\n");
foo(60) -- 60 + 1 => 61 foo(60) -- 60 + 1 => 61
|> bar(130, _) -- 130 - '61 => 69 |> bar(130, _) -- 130 - '61 => 69
|> @write(_); -- '69 => stdout |> @write(_); -- '69 => stdout

15
example/pipe2.hz Normal file
View file

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

View file

@ -1,4 +0,0 @@
fun main: int = do
@read_file("./example/quine.hz") -- Run this from root folder
|> @write(_);
end;

View file

@ -1,9 +0,0 @@
fun main: int = do
@write("Enter your name: ");
let name: string = "";
@read(name);
@write("Hello ");
@write(name);
@write("!");
end;

View file

@ -1,4 +0,0 @@
fun main: int = do
@time()
|> @write(_);
end;

View file

@ -1,2 +0,0 @@
[compiler]
compiler = "clang++"

View file

@ -1,53 +0,0 @@
#pragma once
#include <iostream>
#include <fstream>
#include <string>
template<typename T>
/**
* Read the value from stdin and return it.
*/
T hazure_read() {
T x;
std::cin >> x;
return x;
}
template<typename T>
/**
* 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<char>(file)),
(std::istreambuf_iterator<char>()));
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;
}
}

View file

@ -1,9 +0,0 @@
#pragma once
#include <ctime>
/*
* @brief Get time in seconds since the Epoch.
*/
int hazure_get_time() {
return std::time(0);
}